1 const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.4 2001/10/23 21:48:19 jongfoster 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.4 2001/10/23 21:48:19 jongfoster
39 * Cleaning up error handling in CGI functions - they now send back
40 * a HTML error page and should never cause a FATAL error. (Fixes one
41 * potential source of "denial of service" attacks).
43 * CGI actions file editor that works and is actually useful.
45 * Ability to toggle JunkBuster remotely using a CGI call.
47 * You can turn off both the above features in the main configuration
48 * file, e.g. if you are running a multi-user proxy.
50 * Revision 1.3 2001/10/14 22:12:49 jongfoster
51 * New version of CGI-based actionsfile editor.
52 * Major changes, including:
53 * - Completely new file parser and file output routines
54 * - edit-actions CGI renamed edit-actions-for-url
55 * - All CGIs now need a filename parameter, except for...
56 * - New CGI edit-actions which doesn't need a filename,
57 * to allow you to start the editor up.
58 * - edit-actions-submit now works, and now automatically
59 * redirects you back to the main edit-actions-list handler.
61 * Revision 1.2 2001/09/16 17:05:14 jongfoster
62 * Removing unused #include showarg.h
64 * Revision 1.1 2001/09/16 15:47:37 jongfoster
65 * First version of CGI-based edit interface. This is very much a
66 * work-in-progress, and you can't actually use it to edit anything
67 * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes
71 **********************************************************************/
77 * FIXME: Following includes copied from cgi.c - which are actually needed?
82 #include <sys/types.h>
91 #define snprintf _snprintf
92 #endif /* def _WIN32 */
96 * FIXME: gotta write a snprintf routine. snprintf. You guys kill me.
98 #define snprintf(X,Y,Z) sprintf(X,Z)
104 #include "cgisimple.h"
108 #include "miscutil.h"
111 /* loadcfg.h is for g_bToggleIJB only */
113 const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION;
116 #ifdef FEATURE_CGI_EDIT_ACTIONS
120 struct file_line * next;
128 struct action_spec action[1];
137 /* Add more data types here... e.g.
140 struct url_spec url[1];
144 struct action_spec action[1];
153 #define FILE_LINE_UNPROCESSED 1
154 #define FILE_LINE_BLANK 2
155 #define FILE_LINE_ALIAS_HEADER 3
156 #define FILE_LINE_ALIAS_ENTRY 4
157 #define FILE_LINE_ACTION 5
158 #define FILE_LINE_URL 6
159 #define FILE_LINE_SETTINGS_HEADER 7
160 #define FILE_LINE_SETTINGS_ENTRY 8
161 #define FILE_LINE_DESCRIPTION_HEADER 9
162 #define FILE_LINE_DESCRIPTION_ENTRY 10
167 struct file_line * lines;
168 const char * filename; /* Full pathname - e.g. "/etc/junkbuster/wibble.action" */
169 const char * identifier; /* Filename stub - e.g. "wibble". Use for CGI param. */
170 const char * version_str; /* Last modification time, as a string. For CGI param */
171 unsigned version; /* Last modification time - prevents chaos with
172 * the browser's "back" button. Note that this is a
173 * time_t cast to an unsigned. When comparing, always
174 * cast the time_t to an unsigned, and *NOT* vice-versa.
175 * This may lose the top few bits, but they're not
176 * significant anyway.
178 struct file_line * parse_error; /* On parse error, this is the offending line. */
179 const char * parse_error_text; /* On parse error, this is the problem.
180 * (Statically allocated) */
183 /* FIXME: Following non-static functions should be prototyped in .h or made static */
185 /* Functions to read and write arbitrary config files */
186 jb_err edit_read_file(struct client_state *csp,
187 const struct map *parameters,
190 struct editable_file **pfile);
191 jb_err edit_write_file(struct editable_file * file);
192 void edit_free_file(struct editable_file * file);
194 /* Functions to read and write actions files */
195 jb_err edit_parse_actions_file(struct editable_file * file);
196 jb_err edit_read_actions_file(struct client_state *csp,
197 struct http_response *rsp,
198 const struct map *parameters,
200 struct editable_file **pfile);
203 jb_err cgi_error_modified(struct client_state *csp,
204 struct http_response *rsp,
205 const char *filename);
206 jb_err cgi_error_parse(struct client_state *csp,
207 struct http_response *rsp,
208 struct editable_file *file);
209 jb_err cgi_error_file(struct client_state *csp,
210 struct http_response *rsp,
211 const char *filename);
213 /* Internal arbitrary config file support functions */
214 static jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile);
215 static void edit_free_file_lines(struct file_line * first_line);
216 static jb_err simple_read_line(char **dest, FILE *fp);
217 static jb_err edit_read_line(FILE *fp, char **raw_out, char **prefix_out, char **data_out);
219 /* Internal actions file support functions */
220 static int match_actions_file_header_line(const char * line, const char * name);
221 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue);
223 /* Internal parameter parsing functions */
224 static jb_err get_file_name_param(struct client_state *csp,
225 const struct map *parameters,
226 const char *param_name,
229 const char **pparam);
230 static jb_err get_number_param(struct client_state *csp,
231 const struct map *parameters,
235 /* Internal actionsfile <==> HTML conversion functions */
236 static jb_err map_radio(struct map * exports,
237 const char * optionname,
240 static jb_err actions_to_radio(struct map * exports,
241 const struct action_spec *action);
242 static jb_err actions_from_radio(const struct map * parameters,
243 struct action_spec *action);
246 /*********************************************************************
248 * Function : simple_read_line
250 * Description : Read a single line from a file and return it.
251 * This is basically a version of fgets() that malloc()s
252 * it's own line buffer. Note that the buffer will
253 * always be a multiple of BUFFER_SIZE bytes long.
254 * Therefore if you are going to keep the string for
255 * an extended period of time, you should probably
256 * strdup() it and free() the original, to save memory.
260 * 1 : dest = destination for newly malloc'd pointer to
261 * line data. Will be set to NULL on error.
262 * 2 : fp = File to read from
264 * Returns : JB_ERR_OK on success
265 * JB_ERR_MEMORY on out-of-memory
266 * JB_ERR_FILE on EOF.
268 *********************************************************************/
269 static jb_err simple_read_line(char **dest, FILE *fp)
280 if (NULL == (buf = malloc(BUFFER_SIZE)))
282 return JB_ERR_MEMORY;
291 if ((!fgets(newbuf, BUFFER_SIZE, fp)) || (*newbuf == '\0'))
293 /* (*newbuf == '\0') should never happen unless fgets fails */
306 if ((buf[len - 1] == '\n') || (buf[len - 1] == '\r'))
312 if (NULL == (newbuf = realloc(buf, len + BUFFER_SIZE)))
315 return JB_ERR_MEMORY;
322 /*********************************************************************
324 * Function : edit_read_line
326 * Description : Read a single non-empty line from a file and return
327 * it. Trims comments, leading and trailing whitespace
328 * and respects escaping of newline and comment char.
329 * Provides the line in 2 alternative forms: raw and
331 * - raw is the raw data read from the file. If the
332 * line is not modified, then this should be written
334 * - prefix is any comments and blank lines that were
335 * read from the file. If the line is modified, then
336 * this should be written out to the file followed
337 * by the modified data. (If this string is non-empty
338 * then it will have a newline at the end).
339 * - data is the actual data that will be parsed
340 * further by appropriate routines.
341 * On EOF, the 3 strings will all be set to NULL and
342 * 0 will be returned.
345 * 1 : fp = File to read from
346 * 2 : raw_out = destination for newly malloc'd pointer to
347 * raw line data. May be NULL if you don't want it.
348 * 3 : prefix_out = destination for newly malloc'd pointer to
349 * comments. May be NULL if you don't want it.
350 * 4 : data_out = destination for newly malloc'd pointer to
351 * line data with comments and leading/trailing spaces
352 * removed, and line continuation performed. May be
353 * NULL if you don't want it.
355 * Returns : JB_ERR_OK on success
356 * JB_ERR_MEMORY on out-of-memory
357 * JB_ERR_FILE on EOF.
359 *********************************************************************/
360 static jb_err edit_read_line(FILE *fp, char **raw_out, char **prefix_out, char **data_out)
362 char *p; /* Temporary pointer */
363 char *linebuf; /* Line read from file */
364 char *linestart; /* Start of linebuf, usually first non-whitespace char */
365 char newline[3]; /* Used to store the newline - "\n", "\r", or "\r\n" */
366 int contflag = 0; /* Nonzero for line continuation - i.e. line ends '\' */
367 char *raw; /* String to be stored in raw_out */
368 char *prefix; /* String to be stored in prefix_out */
369 char *data; /* String to be stored in data_out */
370 jb_err rval = JB_ERR_OK;
374 /* Set output parameters to NULL */
388 /* Set string variables to new, empty strings. */
394 if ((raw == NULL) || (prefix == NULL) || (data == NULL))
399 return JB_ERR_MEMORY;
406 /* Main loop. Loop while we need more data & it's not EOF. */
408 while ( (contflag || (*data == '\0'))
409 && (JB_ERR_OK == (rval = simple_read_line(&linebuf, fp))))
411 if (string_append(&raw,linebuf))
416 return JB_ERR_MEMORY;
419 /* Trim off newline */
420 p = linebuf + strlen(linebuf);
421 if ((p != linebuf) && ((p[-1] == '\r') || (p[-1] == '\n')))
424 if ((p != linebuf) && ((p[-1] == '\r') || (p[-1] == '\n')))
432 /* Line continuation? Trim escape and set flag. */
433 contflag = ((p != linebuf) && (*--p == '\\'));
439 /* Trim leading spaces if we're at the start of the line */
443 /* Trim leading spaces */
444 while (*linestart && isspace((int)(unsigned char)*linestart))
450 /* Handle comment characters. */
452 while ((p = strchr(p, '#')) != NULL)
454 /* Found a comment char.. */
455 if ((p != linebuf) && (*(p-1) == '\\'))
457 /* ..and it's escaped, left-shift the line over the escape. */
459 while ((*q = *(q + 1)) != '\0')
463 /* Now scan from just after the "#". */
467 /* Real comment. Save it... */
470 /* Special case: Line only contains a comment, so all the
471 * previous whitespace is considered part of the comment.
472 * Undo the whitespace skipping, if any.
477 string_append(&prefix,p);
478 if (string_append(&prefix,newline))
483 return JB_ERR_MEMORY;
487 /* ... and chop off the rest of the line */
490 } /* END while (there's a # character) */
492 /* Write to the buffer */
495 if (string_append(&data, linestart))
500 return JB_ERR_MEMORY;
505 } /* END while(we need more data) */
507 /* Handle simple_read_line() errors - ignore EOF */
508 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
519 /* Got at least some data */
521 /* Remove trailing whitespace */
534 *prefix_out = prefix;
552 /* EOF and no data there. */
563 /*********************************************************************
565 * Function : edit_write_file
567 * Description : Write a complete file to disk.
570 * 1 : filename = File to write to.
571 * 2 : file = Data structure to write.
573 * Returns : JB_ERR_OK on success
574 * JB_ERR_FILE on error writing to file.
575 * JB_ERR_MEMORY on out of memory
577 *********************************************************************/
578 jb_err edit_write_file(struct editable_file * file)
581 struct file_line * cur_line;
582 struct stat statbuf[1];
583 char version_buf[22]; /* 22 = ceil(log10(2^64)) + 2 = max number of
584 digits in time_t, assuming this is a 64-bit
585 machine, plus null terminator, plus one
589 assert(file->filename);
591 if (NULL == (fp = fopen(file->filename, "wt")))
596 cur_line = file->lines;
597 while (cur_line != NULL)
601 if (fputs(cur_line->raw, fp) < 0)
609 if (cur_line->prefix)
611 if (fputs(cur_line->prefix, fp) < 0)
617 if (cur_line->unprocessed)
619 if (fputs(cur_line->unprocessed, fp) < 0)
624 if (fputs("\n", fp) < 0)
632 /* FIXME: Write data from file->data->whatever */
636 cur_line = cur_line->next;
642 /* Update the version stamp in the file structure, since we just
643 * wrote to the file & changed it's date.
645 if (stat(file->filename, statbuf) < 0)
647 /* Error, probably file not found. */
650 file->version = (unsigned)statbuf->st_mtime;
652 /* Correct file->version_str */
653 freez(file->version_str);
654 snprintf(version_buf, 22, "%u", file->version);
655 version_buf[21] = '\0';
656 file->version_str = strdup(version_buf);
657 if (version_buf == NULL)
659 return JB_ERR_MEMORY;
666 /*********************************************************************
668 * Function : edit_free_file
670 * Description : Free a complete file in memory.
673 * 1 : file = Data structure to free.
677 *********************************************************************/
678 void edit_free_file(struct editable_file * file)
682 /* Silently ignore NULL pointer */
686 edit_free_file_lines(file->lines);
687 freez(file->filename);
688 freez(file->identifier);
689 freez(file->version_str);
691 file->parse_error_text = NULL; /* Statically allocated */
692 file->parse_error = NULL;
698 /*********************************************************************
700 * Function : edit_free_file
702 * Description : Free an entire linked list of file lines.
705 * 1 : first_line = Data structure to free.
709 *********************************************************************/
710 static void edit_free_file_lines(struct file_line * first_line)
712 struct file_line * next_line;
714 while (first_line != NULL)
716 next_line = first_line->next;
717 first_line->next = NULL;
718 freez(first_line->raw);
719 freez(first_line->prefix);
720 freez(first_line->unprocessed);
721 switch(first_line->type)
723 case 0: /* special case if memory zeroed */
724 case FILE_LINE_UNPROCESSED:
725 case FILE_LINE_BLANK:
726 case FILE_LINE_ALIAS_HEADER:
727 case FILE_LINE_SETTINGS_HEADER:
728 case FILE_LINE_DESCRIPTION_HEADER:
729 case FILE_LINE_DESCRIPTION_ENTRY:
730 case FILE_LINE_ALIAS_ENTRY:
732 /* No data is stored for these */
735 case FILE_LINE_ACTION:
736 free_action(first_line->data.action);
739 case FILE_LINE_SETTINGS_ENTRY:
740 freez(first_line->data.setting.name);
741 freez(first_line->data.setting.svalue);
744 /* Should never happen */
748 first_line->type = 0; /* paranoia */
750 first_line = next_line;
755 /*********************************************************************
757 * Function : match_actions_file_header_line
759 * Description : Match an actions file {{header}} line
762 * 1 : line - String from file
763 * 2 : name - Header to match against
765 * Returns : 0 iff they match.
767 *********************************************************************/
768 static int match_actions_file_header_line(const char * line, const char * name)
776 if ((line[0] != '{') || (line[1] != '{'))
782 /* Look for optional whitespace */
783 while ( (*line == ' ') || (*line == '\t') )
788 /* Look for the specified name (case-insensitive) */
790 if (0 != strncmpic(line, name, len))
796 /* Look for optional whitespace */
797 while ( (*line == ' ') || (*line == '\t') )
802 /* Look for "}}" and end of string*/
803 if ((line[0] != '}') || (line[1] != '}') || (line[2] != '\0'))
813 /*********************************************************************
815 * Function : match_actions_file_header_line
817 * Description : Match an actions file {{header}} line
820 * 1 : line - String from file. Must not start with
821 * whitespace (else infinite loop!)
822 * 2 : name - Destination for name
823 * 2 : name - Destination for value
825 * Returns : JB_ERR_OK on success
826 * JB_ERR_MEMORY on out-of-memory
827 * JB_ERR_PARSE if there's no "=" sign, or if there's
828 * nothing before the "=" sign (but empty
829 * values *after* the "=" sign are legal).
831 *********************************************************************/
832 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue)
834 const char * name_end;
835 const char * value_start;
841 assert(*line != ' ');
842 assert(*line != '\t');
847 value_start = strchr(line, '=');
848 if ((value_start == NULL) || (value_start == line))
853 name_end = value_start - 1;
855 /* Eat any whitespace before the '=' */
856 while ((*name_end == ' ') || (*name_end == '\t'))
859 * we already know we must have at least 1 non-ws char
860 * at start of buf - no need to check
865 name_len = name_end - line + 1; /* Length excluding \0 */
866 if (NULL == (*pname = (char *) malloc(name_len + 1)))
868 return JB_ERR_MEMORY;
870 strncpy(*pname, line, name_len);
871 (*pname)[name_len] = '\0';
873 /* Eat any the whitespace after the '=' */
875 while ((*value_start == ' ') || (*value_start == '\t'))
880 if (NULL == (*pvalue = strdup(value_start)))
884 return JB_ERR_MEMORY;
891 /*********************************************************************
893 * Function : edit_parse_actions_file
895 * Description : Parse an actions file in memory.
897 * Passed linked list must have the "data" member
898 * zeroed, and must contain valid "next" and
899 * "unprocessed" fields. The "raw" and "prefix"
900 * fields are ignored, and "type" is just overwritten.
902 * Note that on error the file may have been
906 * 1 : file = Actions file to be parsed in-place.
908 * Returns : JB_ERR_OK on success
909 * JB_ERR_MEMORY on out-of-memory
910 * JB_ERR_PARSE on error
912 *********************************************************************/
913 jb_err edit_parse_actions_file(struct editable_file * file)
915 struct file_line * cur_line;
917 const char * text; /* Text from a line */
918 char * name; /* For lines of the form name=value */
919 char * value; /* For lines of the form name=value */
920 struct action_alias * alias_list = NULL;
921 jb_err err = JB_ERR_OK;
923 /* alias_list contains the aliases defined in this file.
924 * It might be better to use the "file_line.data" fields
925 * in the relavent places instead.
928 cur_line = file->lines;
930 /* A note about blank line support: Blank lines should only
931 * ever occur as the last line in the file. This function
932 * is more forgiving than that - FILE_LINE_BLANK can occur
936 /* Skip leading blanks. Should only happen if file is
937 * empty (which is valid, but pointless).
939 while ( (cur_line != NULL)
940 && (cur_line->unprocessed[0] == '\0') )
943 cur_line->type = FILE_LINE_BLANK;
944 cur_line = cur_line->next;
947 if ( (cur_line != NULL)
948 && (cur_line->unprocessed[0] != '{') )
950 /* File doesn't start with a header */
951 file->parse_error = cur_line;
952 file->parse_error_text = "First (non-comment) line of the file must contain a header.";
956 if ( (cur_line != NULL) && (0 ==
957 match_actions_file_header_line(cur_line->unprocessed, "settings") ) )
959 cur_line->type = FILE_LINE_SETTINGS_HEADER;
961 cur_line = cur_line->next;
962 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
964 if (cur_line->unprocessed[0])
966 cur_line->type = FILE_LINE_SETTINGS_ENTRY;
968 err = split_line_on_equals(cur_line->unprocessed,
969 &cur_line->data.setting.name,
970 &cur_line->data.setting.svalue);
971 if (err == JB_ERR_MEMORY)
975 else if (err != JB_ERR_OK)
977 /* Line does not contain a name=value pair */
978 file->parse_error = cur_line;
979 file->parse_error_text = "Expected a name=value pair on this {{description}} line, but couldn't find one.";
985 cur_line->type = FILE_LINE_BLANK;
987 cur_line = cur_line->next;
991 if ( (cur_line != NULL) && (0 ==
992 match_actions_file_header_line(cur_line->unprocessed, "description") ) )
994 cur_line->type = FILE_LINE_DESCRIPTION_HEADER;
996 cur_line = cur_line->next;
997 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
999 if (cur_line->unprocessed[0])
1001 cur_line->type = FILE_LINE_DESCRIPTION_ENTRY;
1005 cur_line->type = FILE_LINE_BLANK;
1007 cur_line = cur_line->next;
1011 if ( (cur_line != NULL) && (0 ==
1012 match_actions_file_header_line(cur_line->unprocessed, "alias") ) )
1014 cur_line->type = FILE_LINE_ALIAS_HEADER;
1016 cur_line = cur_line->next;
1017 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1019 if (cur_line->unprocessed[0])
1021 /* define an alias */
1022 struct action_alias * new_alias;
1024 cur_line->type = FILE_LINE_ALIAS_ENTRY;
1026 err = split_line_on_equals(cur_line->unprocessed, &name, &value);
1027 if (err == JB_ERR_MEMORY)
1031 else if (err != JB_ERR_OK)
1033 /* Line does not contain a name=value pair */
1034 file->parse_error = cur_line;
1035 file->parse_error_text = "Expected a name=value pair on this {{alias}} line, but couldn't find one.";
1036 return JB_ERR_PARSE;
1039 if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1044 free_alias_list(alias_list);
1045 return JB_ERR_MEMORY;
1048 err = get_actions(value, alias_list, new_alias->action);
1051 /* Invalid action or out of memory */
1055 free_alias_list(alias_list);
1056 if (err == JB_ERR_MEMORY)
1062 /* Line does not contain a name=value pair */
1063 file->parse_error = cur_line;
1064 file->parse_error_text = "This alias does not specify a valid set of actions.";
1065 return JB_ERR_PARSE;
1071 new_alias->name = name;
1074 new_alias->next = alias_list;
1075 alias_list = new_alias;
1079 cur_line->type = FILE_LINE_BLANK;
1081 cur_line = cur_line->next;
1085 /* Header done, process the main part of the file */
1086 while (cur_line != NULL)
1088 /* At this point, (cur_line->unprocessed[0] == '{') */
1089 assert(cur_line->unprocessed[0] == '{');
1090 text = cur_line->unprocessed + 1;
1091 len = strlen(text) - 1;
1092 if (text[len] != '}')
1094 /* No closing } on header */
1095 free_alias_list(alias_list);
1096 file->parse_error = cur_line;
1097 file->parse_error_text = "Headers starting with '{' must have a "
1098 "closing bracket ('}'). Headers starting with two brackets ('{{') "
1099 "must close with two brackets ('}}').";
1100 return JB_ERR_PARSE;
1105 /* An invalid {{ header. */
1106 free_alias_list(alias_list);
1107 file->parse_error = cur_line;
1108 file->parse_error_text = "Unknown or unexpected two-bracket header. "
1109 "Please remember that the system (two-bracket) headers must "
1110 "appear in the order {{settings}}, {{description}}, {{alias}}, "
1111 "and must appear before any actions (one-bracket) headers. "
1112 "Also note that system headers may not be repeated.";
1113 return JB_ERR_PARSE;
1116 while ( (*text == ' ') || (*text == '\t') )
1122 && ( (text[len - 1] == ' ')
1123 || (text[len - 1] == '\t') ) )
1128 cur_line->type = FILE_LINE_ACTION;
1130 /* Remove {} and make copy */
1131 if (NULL == (value = (char *) malloc(len + 1)))
1134 free_alias_list(alias_list);
1135 return JB_ERR_MEMORY;
1137 strncpy(value, text, len);
1141 err = get_actions(value, alias_list, cur_line->data.action);
1144 /* Invalid action or out of memory */
1146 free_alias_list(alias_list);
1147 if (err == JB_ERR_MEMORY)
1153 /* Line does not contain a name=value pair */
1154 file->parse_error = cur_line;
1155 file->parse_error_text = "This header does not specify a valid set of actions.";
1156 return JB_ERR_PARSE;
1160 /* Done with string - it was clobbered anyway */
1163 /* Process next line */
1164 cur_line = cur_line->next;
1166 /* Loop processing URL patterns */
1167 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1169 if (cur_line->unprocessed[0])
1171 /* Could parse URL here, but this isn't currently needed */
1173 cur_line->type = FILE_LINE_URL;
1177 cur_line->type = FILE_LINE_BLANK;
1179 cur_line = cur_line->next;
1181 } /* End main while(cur_line != NULL) loop */
1183 free_alias_list(alias_list);
1189 /*********************************************************************
1191 * Function : edit_read_file_lines
1193 * Description : Read all the lines of a file into memory.
1194 * Handles whitespace, comments and line continuation.
1197 * 1 : fp = File to read from. On return, this will be
1198 * at EOF but it will not have been closed.
1199 * 2 : pfile = Destination for a linked list of file_lines.
1200 * Will be set to NULL on error.
1202 * Returns : JB_ERR_OK on success
1203 * JB_ERR_MEMORY on out-of-memory
1205 *********************************************************************/
1206 jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile)
1208 struct file_line * first_line; /* Keep for return value or to free */
1209 struct file_line * cur_line; /* Current line */
1210 struct file_line * prev_line; /* Entry with prev_line->next = cur_line */
1218 cur_line = first_line = zalloc(sizeof(struct file_line));
1219 if (cur_line == NULL)
1221 return JB_ERR_MEMORY;
1224 cur_line->type = FILE_LINE_UNPROCESSED;
1226 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed);
1229 /* Out of memory or empty file. */
1230 /* Note that empty file is not an error we propogate up */
1232 return ((rval == JB_ERR_FILE) ? JB_ERR_OK : rval);
1237 prev_line = cur_line;
1238 cur_line = prev_line->next = zalloc(sizeof(struct file_line));
1239 if (cur_line == NULL)
1242 edit_free_file_lines(first_line);
1243 return JB_ERR_MEMORY;
1246 cur_line->type = FILE_LINE_UNPROCESSED;
1248 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed);
1249 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
1252 edit_free_file_lines(first_line);
1253 return JB_ERR_MEMORY;
1257 while (rval != JB_ERR_FILE);
1261 /* We allocated one too many - free it */
1262 prev_line->next = NULL;
1265 *pfile = first_line;
1270 /*********************************************************************
1272 * Function : edit_read_file
1274 * Description : Read a complete file into memory.
1275 * Handles CGI parameter parsing. If requested, also
1276 * checks the file's modification timestamp.
1279 * 1 : csp = Current client state (buffers, headers, etc...)
1280 * 2 : parameters = map of cgi parameters.
1281 * 3 : require_version = true to check "ver" parameter.
1282 * 4 : suffix = File extension, e.g. ".action".
1283 * 5 : pfile = Destination for the file. Will be set
1287 * filename : The name of the file to read, without the
1288 * path or ".action" extension.
1289 * ver : (Only if require_version is nonzero)
1290 * Timestamp of the actions file. If wrong, this
1291 * function fails with JB_ERR_MODIFIED.
1293 * Returns : JB_ERR_OK on success
1294 * JB_ERR_MEMORY on out-of-memory
1295 * JB_ERR_CGI_PARAMS if "filename" was not specified
1297 * JB_ERR_FILE if the file cannot be opened or
1299 * JB_ERR_MODIFIED if version checking was requested and
1300 * failed - the file was modified outside
1301 * of this CGI editor instance.
1303 *********************************************************************/
1304 jb_err edit_read_file(struct client_state *csp,
1305 const struct map *parameters,
1306 int require_version,
1308 struct editable_file **pfile)
1310 struct file_line * lines;
1314 const char * identifier;
1315 struct editable_file * file;
1316 unsigned version = 0;
1317 struct stat statbuf[1];
1318 char version_buf[22];
1326 err = get_file_name_param(csp, parameters, "filename", suffix,
1327 &filename, &identifier);
1333 if (stat(filename, statbuf) < 0)
1335 /* Error, probably file not found. */
1339 version = (unsigned) statbuf->st_mtime;
1341 if (require_version)
1343 unsigned specified_version;
1344 err = get_number_param(csp, parameters, "ver", &specified_version);
1351 if (version != specified_version)
1353 return JB_ERR_MODIFIED;
1357 if (NULL == (fp = fopen(filename,"rt")))
1363 err = edit_read_file_lines(fp, &lines);
1373 file = (struct editable_file *) zalloc(sizeof(*file));
1377 edit_free_file_lines(lines);
1381 file->lines = lines;
1382 file->filename = filename;
1383 file->version = version;
1384 file->identifier = strdup(identifier);
1386 if (file->identifier == NULL)
1388 edit_free_file(file);
1389 return JB_ERR_MEMORY;
1392 /* Correct file->version_str */
1393 freez(file->version_str);
1394 snprintf(version_buf, 22, "%u", file->version);
1395 version_buf[21] = '\0';
1396 file->version_str = strdup(version_buf);
1397 if (version_buf == NULL)
1399 edit_free_file(file);
1400 return JB_ERR_MEMORY;
1408 /*********************************************************************
1410 * Function : edit_read_actions_file
1412 * Description : Read a complete actions file into memory.
1413 * Handles CGI parameter parsing. If requested, also
1414 * checks the file's modification timestamp.
1416 * If this function detects an error in the categories
1417 * JB_ERR_FILE, JB_ERR_MODIFIED, or JB_ERR_PARSE,
1418 * then it handles it by filling in the specified
1419 * response structure and returning JB_ERR_FILE.
1422 * 1 : csp = Current client state (buffers, headers, etc...)
1423 * 2 : rsp = HTTP response. Only filled in on error.
1424 * 2 : parameters = map of cgi parameters.
1425 * 3 : require_version = true to check "ver" parameter.
1426 * 4 : pfile = Destination for the file. Will be set
1430 * filename : The name of the actions file to read, without the
1431 * path or ".action" extension.
1432 * ver : (Only if require_version is nonzero)
1433 * Timestamp of the actions file. If wrong, this
1434 * function fails with JB_ERR_MODIFIED.
1436 * Returns : JB_ERR_OK on success
1437 * JB_ERR_MEMORY on out-of-memory
1438 * JB_ERR_CGI_PARAMS if "filename" was not specified
1440 * JB_ERR_FILE if the file does not contain valid data,
1441 * or if file cannot be opened or
1442 * contains no data, or if version
1443 * checking was requested and failed.
1445 *********************************************************************/
1446 jb_err edit_read_actions_file(struct client_state *csp,
1447 struct http_response *rsp,
1448 const struct map *parameters,
1449 int require_version,
1450 struct editable_file **pfile)
1453 struct editable_file *file;
1461 err = edit_read_file(csp, parameters, require_version, ".action", &file);
1464 /* Try to handle if possible */
1465 if (err == JB_ERR_FILE)
1467 err = cgi_error_file(csp, rsp, lookup(parameters, "filename"));
1469 else if (err == JB_ERR_MODIFIED)
1471 err = cgi_error_modified(csp, rsp, lookup(parameters, "filename"));
1473 if (err == JB_ERR_OK)
1476 * Signal to higher-level CGI code that there was a problem but we
1477 * handled it, they should just return JB_ERR_OK.
1484 err = edit_parse_actions_file(file);
1487 if (err == JB_ERR_PARSE)
1489 err = cgi_error_parse(csp, rsp, file);
1490 if (err == JB_ERR_OK)
1493 * Signal to higher-level CGI code that there was a problem but we
1494 * handled it, they should just return JB_ERR_OK.
1499 edit_free_file(file);
1508 /*********************************************************************
1510 * Function : get_file_name_param
1512 * Description : Get the name of the file to edit from the parameters
1513 * passed to a CGI function. This function handles
1514 * security checks such as blocking urls containing
1515 * "/" or ".", prepending the config file directory,
1516 * and adding the specified suffix.
1518 * (This is an essential security check, otherwise
1519 * users may be able to pass "../../../etc/passwd"
1520 * and overwrite the password file [linux], "prn:"
1521 * and print random data [Windows], etc...)
1523 * This function only allows filenames contining the
1524 * characters '-', '_', 'A'-'Z', 'a'-'z', and '0'-'9'.
1525 * That's probably too restrictive but at least it's
1529 * 1 : csp = Current client state (buffers, headers, etc...)
1530 * 2 : parameters = map of cgi parameters
1531 * 3 : suffix = File extension, e.g. ".actions"
1532 * 4 : pfilename = destination for full filename. Caller
1533 * free()s. Set to NULL on error.
1534 * 5 : pparam = destination for partial filename,
1535 * suitable for use in another URL. Allocated as part
1536 * of the map "parameters", so don't free it.
1537 * Set to NULL if not specified.
1539 * Returns : JB_ERR_OK on success
1540 * JB_ERR_MEMORY on out-of-memory
1541 * JB_ERR_CGI_PARAMS if "filename" was not specified
1544 *********************************************************************/
1545 static jb_err get_file_name_param(struct client_state *csp,
1546 const struct map *parameters,
1547 const char *param_name,
1550 const char **pparam)
1568 param = lookup(parameters, param_name);
1571 return JB_ERR_CGI_PARAMS;
1576 len = strlen(param);
1577 if (len >= FILENAME_MAX)
1580 return JB_ERR_CGI_PARAMS;
1583 /* Check every character to see if it's legal */
1585 while ((ch = *s++) != '\0')
1587 if ( ((ch < 'A') || (ch > 'Z'))
1588 && ((ch < 'a') || (ch > 'z'))
1589 && ((ch < '0') || (ch > '9'))
1593 /* Probable hack attempt. */
1594 return JB_ERR_CGI_PARAMS;
1598 /* Append extension */
1599 name = malloc(len + strlen(suffix) + 1);
1602 return JB_ERR_MEMORY;
1604 strcpy(name, param);
1605 strcpy(name + len, suffix);
1608 fullpath = make_path(csp->config->confdir, name);
1610 if (fullpath == NULL)
1612 return JB_ERR_MEMORY;
1616 *pfilename = fullpath;
1622 /*********************************************************************
1624 * Function : get_number_param
1626 * Description : Get a non-negative integer from the parameters
1627 * passed to a CGI function.
1630 * 1 : csp = Current client state (buffers, headers, etc...)
1631 * 2 : parameters = map of cgi parameters
1632 * 3 : name = Name of CGI parameter to read
1633 * 4 : pvalue = destination for value.
1634 * Set to -1 on error.
1636 * Returns : JB_ERR_OK on success
1637 * JB_ERR_MEMORY on out-of-memory
1638 * JB_ERR_CGI_PARAMS if the parameter was not specified
1641 *********************************************************************/
1642 static jb_err get_number_param(struct client_state *csp,
1643 const struct map *parameters,
1658 param = lookup(parameters, name);
1661 return JB_ERR_CGI_PARAMS;
1664 /* We don't use atoi because I want to check this carefully... */
1667 while ((ch = *param++) != '\0')
1669 if ((ch < '0') || (ch > '9'))
1671 return JB_ERR_CGI_PARAMS;
1678 * <limits.h> defines UINT_MAX
1680 * (UINT_MAX - ch) / 10 is the largest number that
1681 * can be safely multiplied by 10 then have ch added.
1683 if (value > ((UINT_MAX - (unsigned)ch) / 10U))
1685 return JB_ERR_CGI_PARAMS;
1688 value = value * 10 + ch;
1698 /*********************************************************************
1700 * Function : map_radio
1702 * Description : Map a set of radio button values. E.g. if you have
1703 * 3 radio buttons, declare them as:
1704 * <option type="radio" name="xyz" @xyz-a@>
1705 * <option type="radio" name="xyz" @xyz-b@>
1706 * <option type="radio" name="xyz" @xyz-c@>
1707 * Then map one of the @xyz-?@ variables to "checked"
1708 * and all the others to empty by calling:
1709 * map_radio(exports, "xyz", "abc", sel)
1710 * Where 'sel' is 'a', 'b', or 'c'.
1713 * 1 : exports = Exports map to modify.
1714 * 2 : optionname = name for map
1715 * 3 : values = null-terminated list of values;
1716 * 4 : value = Selected value.
1718 * CGI Parameters : None
1720 * Returns : JB_ERR_OK on success
1721 * JB_ERR_MEMORY on out-of-memory
1723 *********************************************************************/
1724 static jb_err map_radio(struct map * exports,
1725 const char * optionname,
1726 const char * values,
1738 len = strlen(optionname);
1739 buf = malloc(len + 3);
1742 return JB_ERR_MEMORY;
1745 strcpy(buf, optionname);
1750 while ((c = *values++) != '\0')
1755 if (map(exports, buf, 1, "", 1))
1758 return JB_ERR_MEMORY;
1764 if (map(exports, buf, 0, "checked", 1))
1767 return JB_ERR_MEMORY;
1774 /*********************************************************************
1776 * Function : actions_to_radio
1778 * Description : Converts a actionsfile entry into settings for
1779 * radio buttons and edit boxes on a HTML form.
1782 * 1 : exports = List of substitutions to add to.
1783 * 2 : action = Action to read
1785 * Returns : JB_ERR_OK on success
1786 * JB_ERR_MEMORY on out-of-memory
1788 *********************************************************************/
1789 static jb_err actions_to_radio(struct map * exports,
1790 const struct action_spec *action)
1792 unsigned mask = action->mask;
1793 unsigned add = action->add;
1801 mask = action->mask;
1804 /* sanity - prevents "-feature +feature" */
1808 #define DEFINE_ACTION_BOOL(name, bit) \
1809 if (!(mask & bit)) \
1811 current_mode = 'n'; \
1813 else if (add & bit) \
1815 current_mode = 'y'; \
1819 current_mode = 'x'; \
1821 if (map_radio(exports, name, "ynx", current_mode)) \
1823 return JB_ERR_MEMORY; \
1826 #define DEFINE_ACTION_STRING(name, bit, index) \
1827 DEFINE_ACTION_BOOL(name, bit); \
1830 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default) \
1833 checked = !strcmp(action->string[index], value); \
1837 checked = is_default; \
1839 mapped_param |= checked; \
1840 if (map(exports, name "-param-" value, 1, (checked ? "checked" : ""), 1)) \
1842 return JB_ERR_MEMORY; \
1845 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val) \
1846 if (map(exports, name "-param-custom", 1, \
1847 ((!mapped_param) ? "checked" : ""), 1)) \
1849 return JB_ERR_MEMORY; \
1851 if (map(exports, name "-param", 1, \
1852 (((add & bit) && !mapped_param) ? \
1853 action->string[index] : default_val), 1)) \
1855 return JB_ERR_MEMORY; \
1858 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val) \
1859 if (map(exports, name "-param", 1, \
1860 ((add & bit) ? action->string[index] : default_val), 1)) \
1862 return JB_ERR_MEMORY; \
1865 #define DEFINE_ACTION_MULTI(name, index) \
1866 if (action->multi_add[index]->first) \
1868 current_mode = 'y'; \
1870 else if (action->multi_remove_all[index]) \
1872 current_mode = 'n'; \
1874 else if (action->multi_remove[index]->first) \
1876 current_mode = 'y'; \
1880 current_mode = 'x'; \
1882 if (map_radio(exports, name, "ynx", current_mode)) \
1884 return JB_ERR_MEMORY; \
1887 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1889 #include "actionlist.h"
1891 #undef DEFINE_ACTION_MULTI
1892 #undef DEFINE_ACTION_STRING
1893 #undef DEFINE_ACTION_BOOL
1894 #undef DEFINE_ACTION_ALIAS
1895 #undef DEFINE_CGI_PARAM_CUSTOM
1896 #undef DEFINE_CGI_PARAM_RADIO
1897 #undef DEFINE_CGI_PARAM_NO_RADIO
1903 /*********************************************************************
1905 * Function : actions_from_radio
1907 * Description : Converts a map of parameters passed to a CGI function
1908 * into an actionsfile entry.
1911 * 1 : parameters = parameters to the CGI call
1912 * 2 : action = Action to change. Must be valid before
1913 * the call, actions not specified will be
1916 * Returns : JB_ERR_OK on success
1917 * JB_ERR_MEMORY on out-of-memory
1919 *********************************************************************/
1920 static jb_err actions_from_radio(const struct map * parameters,
1921 struct action_spec *action)
1930 #define DEFINE_ACTION_BOOL(name, bit) \
1931 if (NULL != (param = lookup(parameters, name))) \
1933 ch = toupper((int)param[0]); \
1936 action->add |= bit; \
1937 action->mask |= bit; \
1939 else if (ch == 'N') \
1941 action->add &= ~bit; \
1942 action->mask &= ~bit; \
1944 else if (ch == 'X') \
1946 action->add &= ~bit; \
1947 action->mask |= bit; \
1951 #define DEFINE_ACTION_STRING(name, bit, index) \
1952 if (NULL != (param = lookup(parameters, name))) \
1954 ch = toupper((int)param[0]); \
1957 param = lookup(parameters, name "-mode"); \
1958 if ((*param == '\0') || (0 == strcmp(param, "CUSTOM"))) \
1960 param = lookup(parameters, name "-param"); \
1962 if (*param != '\0') \
1964 if (NULL == (param_dup = strdup(param))) \
1966 return JB_ERR_MEMORY; \
1968 freez(action->string[index]); \
1969 action->add |= bit; \
1970 action->mask |= bit; \
1971 action->string[index] = param_dup; \
1974 else if (ch == 'N') \
1976 if (action->add & bit) \
1978 freez(action->string[index]); \
1980 action->add &= ~bit; \
1981 action->mask &= ~bit; \
1983 else if (ch == 'X') \
1985 if (action->add & bit) \
1987 freez(action->string[index]); \
1989 action->add &= ~bit; \
1990 action->mask |= bit; \
1994 #define DEFINE_ACTION_MULTI(name, index) \
1995 if (NULL != (param = lookup(parameters, name))) \
1997 ch = toupper((int)param[0]); \
2002 else if (ch == 'N') \
2004 list_remove_all(action->multi_add[index]); \
2005 list_remove_all(action->multi_remove[index]); \
2006 action->multi_remove_all[index] = 1; \
2008 else if (ch == 'X') \
2010 list_remove_all(action->multi_add[index]); \
2011 list_remove_all(action->multi_remove[index]); \
2012 action->multi_remove_all[index] = 0; \
2016 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val)
2017 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default)
2018 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val)
2020 #define DEFINE_ACTION_ALIAS 0 /* No aliases for URL parsing */
2022 #include "actionlist.h"
2024 #undef DEFINE_ACTION_MULTI
2025 #undef DEFINE_ACTION_STRING
2026 #undef DEFINE_ACTION_BOOL
2027 #undef DEFINE_ACTION_ALIAS
2028 #undef DEFINE_CGI_PARAM_CUSTOM
2029 #undef DEFINE_CGI_PARAM_RADIO
2030 #undef DEFINE_CGI_PARAM_NO_RADIO
2036 /*********************************************************************
2038 * Function : cgi_error_modified
2040 * Description : CGI function that is called when a file is modified
2041 * outside the CGI editor.
2044 * 1 : csp = Current client state (buffers, headers, etc...)
2045 * 2 : rsp = http_response data structure for output
2046 * 3 : filename = The file that was modified.
2048 * CGI Parameters : none
2050 * Returns : JB_ERR_OK on success
2051 * JB_ERR_MEMORY on out-of-memory error.
2053 *********************************************************************/
2054 jb_err cgi_error_modified(struct client_state *csp,
2055 struct http_response *rsp,
2056 const char *filename)
2058 struct map *exports;
2065 if (NULL == (exports = default_exports(csp, NULL)))
2067 return JB_ERR_MEMORY;
2070 err = map(exports, "filename", 1, filename, 1);
2077 return template_fill_for_cgi(csp, "cgi-error-modified", exports, rsp);
2081 /*********************************************************************
2083 * Function : cgi_error_parse
2085 * Description : CGI function that is called when a file cannot
2086 * be parsed by the CGI editor.
2089 * 1 : csp = Current client state (buffers, headers, etc...)
2090 * 2 : rsp = http_response data structure for output
2091 * 3 : file = The file that was modified.
2093 * CGI Parameters : none
2095 * Returns : JB_ERR_OK on success
2096 * JB_ERR_MEMORY on out-of-memory error.
2098 *********************************************************************/
2099 jb_err cgi_error_parse(struct client_state *csp,
2100 struct http_response *rsp,
2101 struct editable_file *file)
2103 struct map *exports;
2105 struct file_line *cur_line;
2111 if (NULL == (exports = default_exports(csp, NULL)))
2113 return JB_ERR_MEMORY;
2116 err = map(exports, "filename", 1, file->identifier, 1);
2117 err = err || map(exports, "parse-error", 1, file->parse_error_text, 1);
2119 cur_line = file->parse_error;
2122 err = err || map(exports, "line-raw", 1, html_encode(cur_line->raw), 0);
2123 err = err || map(exports, "line-data", 1, html_encode(cur_line->unprocessed), 0);
2131 return template_fill_for_cgi(csp, "cgi-error-parse", exports, rsp);
2135 /*********************************************************************
2137 * Function : cgi_error_file
2139 * Description : CGI function that is called when a file cannot be
2140 * opened by the CGI editor.
2143 * 1 : csp = Current client state (buffers, headers, etc...)
2144 * 2 : rsp = http_response data structure for output
2145 * 3 : filename = The file that was modified.
2147 * CGI Parameters : none
2149 * Returns : JB_ERR_OK on success
2150 * JB_ERR_MEMORY on out-of-memory error.
2152 *********************************************************************/
2153 jb_err cgi_error_file(struct client_state *csp,
2154 struct http_response *rsp,
2155 const char *filename)
2157 struct map *exports;
2164 if (NULL == (exports = default_exports(csp, NULL)))
2166 return JB_ERR_MEMORY;
2169 err = map(exports, "filename", 1, filename, 1);
2176 return template_fill_for_cgi(csp, "cgi-error-file", exports, rsp);
2180 /*********************************************************************
2182 * Function : cgi_error_bad_param
2184 * Description : CGI function that is called if the parameters
2185 * (query string) for a CGI were wrong.
2188 * 1 : csp = Current client state (buffers, headers, etc...)
2189 * 2 : rsp = http_response data structure for output
2191 * CGI Parameters : none
2193 * Returns : JB_ERR_OK on success
2194 * JB_ERR_MEMORY on out-of-memory error.
2196 *********************************************************************/
2197 jb_err cgi_error_disabled(struct client_state *csp,
2198 struct http_response *rsp)
2200 struct map *exports;
2205 if (NULL == (exports = default_exports(csp, NULL)))
2207 return JB_ERR_MEMORY;
2210 return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp);
2214 /*********************************************************************
2216 * Function : cgi_edit_actions
2218 * Description : CGI function that allows the user to choose which
2219 * actions file to edit.
2222 * 1 : csp = Current client state (buffers, headers, etc...)
2223 * 2 : rsp = http_response data structure for output
2224 * 3 : parameters = map of cgi parameters
2226 * CGI Parameters : None
2228 * Returns : JB_ERR_OK on success
2229 * JB_ERR_MEMORY on out-of-memory error
2231 *********************************************************************/
2232 jb_err cgi_edit_actions(struct client_state *csp,
2233 struct http_response *rsp,
2234 const struct map *parameters)
2237 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2239 return cgi_error_disabled(csp, rsp);
2242 /* FIXME: Incomplete */
2243 rsp->status = strdup("302 Local Redirect from Junkbuster");
2244 if (rsp->status == NULL)
2246 return JB_ERR_MEMORY;
2248 if (enlist_unique_header(rsp->headers, "Location", "http://ijbswa.sourceforge.net/config/edit-actions-list?filename=edit"))
2252 return JB_ERR_MEMORY;
2259 /*********************************************************************
2261 * Function : cgi_edit_actions_list
2263 * Description : CGI function that edits the actions list.
2264 * FIXME: This function shouldn't FATAL ever.
2265 * FIXME: This function doesn't check the retval of map()
2267 * 1 : csp = Current client state (buffers, headers, etc...)
2268 * 2 : rsp = http_response data structure for output
2269 * 3 : parameters = map of cgi parameters
2271 * CGI Parameters : filename
2273 * Returns : JB_ERR_OK on success
2274 * JB_ERR_MEMORY on out-of-memory
2275 * JB_ERR_FILE if the file cannot be opened or
2277 * JB_ERR_CGI_PARAMS if "filename" was not specified
2280 *********************************************************************/
2281 jb_err cgi_edit_actions_list(struct client_state *csp,
2282 struct http_response *rsp,
2283 const struct map *parameters)
2285 char * section_template;
2286 char * url_template;
2291 struct map * exports;
2292 struct map * section_exports;
2293 struct map * url_exports;
2294 struct editable_file * file;
2295 struct file_line * cur_line;
2296 int line_number = 0;
2300 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2302 return cgi_error_disabled(csp, rsp);
2305 err = edit_read_actions_file(csp, rsp, parameters, 0, &file);
2308 /* No filename specified, can't read file, or out of memory. */
2309 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2312 if (NULL == (exports = default_exports(csp, NULL)))
2314 edit_free_file(file);
2315 return JB_ERR_MEMORY;
2318 err = map(exports, "filename", 1, file->identifier, 1);
2319 err = err || map(exports, "ver", 1, file->version_str, 1);
2322 edit_free_file(file);
2327 /* Should do all global exports above this point */
2329 err = template_load(csp, §ion_template, "edit-actions-list-section");
2332 edit_free_file(file);
2334 if (err == JB_ERR_FILE)
2336 return cgi_error_no_template(csp, rsp, "edit-actions-list-section");
2341 err = template_load(csp, &url_template, "edit-actions-list-url");
2344 free(section_template);
2345 edit_free_file(file);
2347 if (err == JB_ERR_FILE)
2349 return cgi_error_no_template(csp, rsp, "edit-actions-list-url");
2354 err = template_fill(§ion_template, exports);
2358 edit_free_file(file);
2364 err = template_fill(&url_template, exports);
2367 free(section_template);
2368 edit_free_file(file);
2373 /* Find start of actions in file */
2374 cur_line = file->lines;
2376 while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION))
2378 cur_line = cur_line->next;
2382 if (NULL == (sections = strdup("")))
2384 free(section_template);
2386 edit_free_file(file);
2388 return JB_ERR_MEMORY;
2391 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_ACTION))
2393 if (NULL == (section_exports = new_map()))
2396 free(section_template);
2398 edit_free_file(file);
2400 return JB_ERR_MEMORY;
2403 snprintf(buf, 50, "%d", line_number);
2404 err = map(section_exports, "sectionid", 1, buf, 1);
2406 err = err || map(section_exports, "actions", 1,
2407 actions_to_html(cur_line->data.action), 0);
2409 if ((cur_line->next != NULL) && (cur_line->next->type == FILE_LINE_URL))
2411 /* This section contains at least one URL, don't allow delete */
2412 err = err || map_block_killer(section_exports, "empty-section");
2418 free(section_template);
2420 edit_free_file(file);
2422 free_map(section_exports);
2426 /* Should do all section-specific exports above this point */
2428 if (NULL == (urls = strdup("")))
2431 free(section_template);
2433 edit_free_file(file);
2435 free_map(section_exports);
2436 return JB_ERR_MEMORY;
2441 cur_line = cur_line->next;
2444 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL))
2446 if (NULL == (url_exports = new_map()))
2450 free(section_template);
2452 edit_free_file(file);
2454 free_map(section_exports);
2455 return JB_ERR_MEMORY;
2458 snprintf(buf, 50, "%d", line_number);
2459 err = map(url_exports, "urlid", 1, buf, 1);
2461 snprintf(buf, 50, "%d", url_1_2);
2462 err = err || map(url_exports, "url-1-2", 1, buf, 1);
2464 err = err || map(url_exports, "url", 1,
2465 html_encode(cur_line->unprocessed), 0);
2471 free(section_template);
2473 edit_free_file(file);
2475 free_map(section_exports);
2476 free_map(url_exports);
2480 if (NULL == (s = strdup(url_template)))
2484 free(section_template);
2486 edit_free_file(file);
2488 free_map(section_exports);
2489 free_map(url_exports);
2490 return JB_ERR_MEMORY;
2493 err = template_fill(&s, section_exports);
2494 err = err || template_fill(&s, url_exports);
2495 err = err || string_append(&urls, s);
2496 free_map(url_exports);
2503 free(section_template);
2505 edit_free_file(file);
2507 free_map(section_exports);
2511 url_1_2 = 3 - url_1_2;
2513 cur_line = cur_line->next;
2517 err = map(section_exports, "urls", 1, urls, 0);
2522 free(section_template);
2524 edit_free_file(file);
2526 free_map(section_exports);
2530 /* Could also do section-specific exports here, but it wouldn't be as fast */
2532 if (NULL == (s = strdup(section_template)))
2535 free(section_template);
2537 edit_free_file(file);
2539 free_map(section_exports);
2540 return JB_ERR_MEMORY;
2543 err = template_fill(&s, section_exports);
2544 err = err || string_append(§ions, s);
2546 free_map(section_exports);
2551 free(section_template);
2553 edit_free_file(file);
2559 edit_free_file(file);
2560 free(section_template);
2563 err = map(exports, "sections", 1, sections, 0);
2570 /* Could also do global exports here, but it wouldn't be as fast */
2572 return template_fill_for_cgi(csp, "edit-actions-list", exports, rsp);
2576 /*********************************************************************
2578 * Function : cgi_edit_actions
2580 * Description : CGI function that edits the Actions list.
2583 * 1 : csp = Current client state (buffers, headers, etc...)
2584 * 2 : rsp = http_response data structure for output
2585 * 3 : parameters = map of cgi parameters
2587 * CGI Parameters : None
2589 * Returns : JB_ERR_OK on success
2590 * JB_ERR_MEMORY on out-of-memory
2591 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2592 * specified or not valid.
2594 *********************************************************************/
2595 jb_err cgi_edit_actions_for_url(struct client_state *csp,
2596 struct http_response *rsp,
2597 const struct map *parameters)
2599 struct map * exports;
2601 struct editable_file * file;
2602 struct file_line * cur_line;
2603 unsigned line_number;
2606 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2608 return cgi_error_disabled(csp, rsp);
2611 err = get_number_param(csp, parameters, "section", §ionid);
2617 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2620 /* No filename specified, can't read file, modified, or out of memory. */
2621 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2624 cur_line = file->lines;
2626 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2628 cur_line = cur_line->next;
2631 if ( (cur_line == NULL)
2632 || (line_number != sectionid)
2634 || (cur_line->type != FILE_LINE_ACTION))
2636 /* Invalid "sectionid" parameter */
2637 edit_free_file(file);
2638 return JB_ERR_CGI_PARAMS;
2641 if (NULL == (exports = default_exports(csp, NULL)))
2643 edit_free_file(file);
2644 return JB_ERR_MEMORY;
2647 err = map(exports, "filename", 1, file->identifier, 1);
2648 err = err || map(exports, "ver", 1, file->version_str, 1);
2649 err = err || map(exports, "section", 1, lookup(parameters, "section"), 1);
2651 err = err || actions_to_radio(exports, cur_line->data.action);
2653 edit_free_file(file);
2661 return template_fill_for_cgi(csp, "edit-actions-for-url", exports, rsp);
2665 /*********************************************************************
2667 * Function : cgi_edit_actions_submit
2669 * Description : CGI function that actually edits the Actions list.
2672 * 1 : csp = Current client state (buffers, headers, etc...)
2673 * 2 : rsp = http_response data structure for output
2674 * 3 : parameters = map of cgi parameters
2676 * CGI Parameters : None
2678 * Returns : JB_ERR_OK on success
2679 * JB_ERR_MEMORY on out-of-memory
2680 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2681 * specified or not valid.
2683 *********************************************************************/
2684 jb_err cgi_edit_actions_submit(struct client_state *csp,
2685 struct http_response *rsp,
2686 const struct map *parameters)
2692 struct editable_file * file;
2693 struct file_line * cur_line;
2698 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2700 return cgi_error_disabled(csp, rsp);
2703 err = get_number_param(csp, parameters, "section", §ionid);
2709 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2712 /* No filename specified, can't read file, modified, or out of memory. */
2713 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2716 cur_line = file->lines;
2718 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2720 cur_line = cur_line->next;
2723 if ( (cur_line == NULL)
2724 || (line_number != sectionid)
2726 || (cur_line->type != FILE_LINE_ACTION))
2728 /* Invalid "sectionid" parameter */
2729 edit_free_file(file);
2730 return JB_ERR_CGI_PARAMS;
2733 err = actions_from_radio(parameters, cur_line->data.action);
2737 edit_free_file(file);
2741 if (NULL == (actiontext = actions_to_text(cur_line->data.action)))
2744 edit_free_file(file);
2745 return JB_ERR_MEMORY;
2748 len = strlen(actiontext);
2752 * Empty action - must special-case this.
2753 * Simply setting len to 1 is sufficient...
2758 if (NULL == (newtext = malloc(len + 2)))
2762 edit_free_file(file);
2763 return JB_ERR_MEMORY;
2765 strcpy(newtext, actiontext);
2769 newtext[len + 1] = '\0';
2771 freez(cur_line->raw);
2772 freez(cur_line->unprocessed);
2773 cur_line->unprocessed = newtext;
2775 err = edit_write_file(file);
2778 /* Error writing file */
2779 edit_free_file(file);
2783 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
2784 string_append(&target, file->identifier);
2786 edit_free_file(file);
2791 return JB_ERR_MEMORY;
2794 rsp->status = strdup("302 Local Redirect from Junkbuster");
2795 if (rsp->status == NULL)
2798 return JB_ERR_MEMORY;
2800 err = enlist_unique_header(rsp->headers, "Location", target);
2807 /*********************************************************************
2809 * Function : cgi_edit_actions_url
2811 * Description : CGI function that actually edits a URL pattern in
2815 * 1 : csp = Current client state (buffers, headers, etc...)
2816 * 2 : rsp = http_response data structure for output
2817 * 3 : parameters = map of cgi parameters
2820 * filename : Identifies the file to edit
2821 * ver : File's last-modified time
2822 * section : Line number of section to edit
2823 * pattern : Line number of pattern to edit
2824 * newval : New value for pattern
2826 * Returns : JB_ERR_OK on success
2827 * JB_ERR_MEMORY on out-of-memory
2828 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2829 * specified or not valid.
2831 *********************************************************************/
2832 jb_err cgi_edit_actions_url(struct client_state *csp,
2833 struct http_response *rsp,
2834 const struct map *parameters)
2838 const char * newval;
2840 struct editable_file * file;
2841 struct file_line * cur_line;
2842 unsigned line_number;
2846 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2848 return cgi_error_disabled(csp, rsp);
2851 err = get_number_param(csp, parameters, "section", §ionid);
2852 err = err || get_number_param(csp, parameters, "pattern", &patternid);
2858 newval = lookup(parameters, "newval");
2860 if ((*newval == '\0') || (sectionid < 1U) || (patternid < 1U))
2862 return JB_ERR_CGI_PARAMS;
2865 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2868 /* No filename specified, can't read file, modified, or out of memory. */
2869 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2873 cur_line = file->lines;
2875 while ((cur_line != NULL) && (line_number < sectionid))
2877 cur_line = cur_line->next;
2881 if ( (cur_line == NULL)
2882 || (cur_line->type != FILE_LINE_ACTION))
2884 /* Invalid "sectionid" parameter */
2885 edit_free_file(file);
2886 return JB_ERR_CGI_PARAMS;
2889 while (line_number < patternid)
2891 cur_line = cur_line->next;
2894 if ( (cur_line == NULL)
2895 || ( (cur_line->type != FILE_LINE_URL)
2896 && (cur_line->type != FILE_LINE_BLANK) ) )
2898 /* Invalid "patternid" parameter */
2899 edit_free_file(file);
2900 return JB_ERR_CGI_PARAMS;
2904 if (cur_line->type != FILE_LINE_URL)
2906 /* Invalid "patternid" parameter */
2907 edit_free_file(file);
2908 return JB_ERR_CGI_PARAMS;
2911 /* At this point, the line to edit is in cur_line */
2913 new_pattern = strdup(newval);
2914 if (NULL == new_pattern)
2916 edit_free_file(file);
2917 return JB_ERR_MEMORY;
2920 freez(cur_line->raw);
2921 freez(cur_line->unprocessed);
2922 cur_line->unprocessed = new_pattern;
2924 err = edit_write_file(file);
2927 /* Error writing file */
2928 edit_free_file(file);
2932 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
2933 string_append(&target, file->identifier);
2935 edit_free_file(file);
2940 return JB_ERR_MEMORY;
2943 rsp->status = strdup("302 Local Redirect from Junkbuster");
2944 if (rsp->status == NULL)
2947 return JB_ERR_MEMORY;
2949 err = enlist_unique_header(rsp->headers, "Location", target);
2956 /*********************************************************************
2958 * Function : cgi_edit_actions_add_url
2960 * Description : CGI function that actually adds a URL pattern to
2964 * 1 : csp = Current client state (buffers, headers, etc...)
2965 * 2 : rsp = http_response data structure for output
2966 * 3 : parameters = map of cgi parameters
2969 * filename : Identifies the file to edit
2970 * ver : File's last-modified time
2971 * section : Line number of section to edit
2972 * newval : New pattern
2974 * Returns : JB_ERR_OK on success
2975 * JB_ERR_MEMORY on out-of-memory
2976 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2977 * specified or not valid.
2979 *********************************************************************/
2980 jb_err cgi_edit_actions_add_url(struct client_state *csp,
2981 struct http_response *rsp,
2982 const struct map *parameters)
2986 const char * newval;
2988 struct file_line * new_line;
2989 struct editable_file * file;
2990 struct file_line * cur_line;
2991 unsigned line_number;
2995 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2997 return cgi_error_disabled(csp, rsp);
3000 err = get_number_param(csp, parameters, "section", §ionid);
3006 newval = lookup(parameters, "newval");
3008 if ((*newval == '\0') || (sectionid < 1U) || (patternid < 1U))
3010 return JB_ERR_CGI_PARAMS;
3013 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3016 /* No filename specified, can't read file, modified, or out of memory. */
3017 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3021 cur_line = file->lines;
3023 while ((cur_line != NULL) && (line_number < sectionid))
3025 cur_line = cur_line->next;
3029 if ( (cur_line == NULL)
3030 || (cur_line->type != FILE_LINE_ACTION))
3032 /* Invalid "sectionid" parameter */
3033 edit_free_file(file);
3034 return JB_ERR_CGI_PARAMS;
3037 /* At this point, the section header is in cur_line - add after this. */
3039 new_pattern = strdup(newval);
3040 if (NULL == new_pattern)
3042 edit_free_file(file);
3043 return JB_ERR_MEMORY;
3046 /* Allocate the new line */
3047 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3048 if (new_line == NULL)
3051 edit_free_file(file);
3052 return JB_ERR_MEMORY;
3055 /* Fill in the data members of the new line */
3056 new_line->raw = NULL;
3057 new_line->prefix = NULL;
3058 new_line->unprocessed = new_pattern;
3059 new_line->type = FILE_LINE_URL;
3061 /* Link new_line into the list, after cur_line */
3062 new_line->next = cur_line->next;
3063 cur_line->next = new_line;
3065 /* Done making changes, now commit */
3067 err = edit_write_file(file);
3070 /* Error writing file */
3071 edit_free_file(file);
3075 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3076 string_append(&target, file->identifier);
3078 edit_free_file(file);
3083 return JB_ERR_MEMORY;
3086 rsp->status = strdup("302 Local Redirect from Junkbuster");
3087 if (rsp->status == NULL)
3090 return JB_ERR_MEMORY;
3092 err = enlist_unique_header(rsp->headers, "Location", target);
3099 /*********************************************************************
3101 * Function : cgi_edit_actions_remove_url
3103 * Description : CGI function that actually removes a URL pattern from
3107 * 1 : csp = Current client state (buffers, headers, etc...)
3108 * 2 : rsp = http_response data structure for output
3109 * 3 : parameters = map of cgi parameters
3112 * filename : Identifies the file to edit
3113 * ver : File's last-modified time
3114 * section : Line number of section to edit
3115 * pattern : Line number of pattern to edit
3117 * Returns : JB_ERR_OK on success
3118 * JB_ERR_MEMORY on out-of-memory
3119 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3120 * specified or not valid.
3122 *********************************************************************/
3123 jb_err cgi_edit_actions_remove_url(struct client_state *csp,
3124 struct http_response *rsp,
3125 const struct map *parameters)
3129 struct editable_file * file;
3130 struct file_line * cur_line;
3131 struct file_line * prev_line;
3132 unsigned line_number;
3136 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3138 return cgi_error_disabled(csp, rsp);
3141 err = get_number_param(csp, parameters, "section", §ionid);
3142 err = err || get_number_param(csp, parameters, "pattern", &patternid);
3149 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3152 /* No filename specified, can't read file, modified, or out of memory. */
3153 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3157 cur_line = file->lines;
3159 while ((cur_line != NULL) && (line_number < sectionid))
3161 cur_line = cur_line->next;
3165 if ( (cur_line == NULL)
3166 || (cur_line->type != FILE_LINE_ACTION))
3168 /* Invalid "sectionid" parameter */
3169 edit_free_file(file);
3170 return JB_ERR_CGI_PARAMS;
3174 while (line_number < patternid)
3176 prev_line = cur_line;
3177 cur_line = cur_line->next;
3180 if ( (cur_line == NULL)
3181 || ( (cur_line->type != FILE_LINE_URL)
3182 && (cur_line->type != FILE_LINE_BLANK) ) )
3184 /* Invalid "patternid" parameter */
3185 edit_free_file(file);
3186 return JB_ERR_CGI_PARAMS;
3190 if (cur_line->type != FILE_LINE_URL)
3192 /* Invalid "patternid" parameter */
3193 edit_free_file(file);
3194 return JB_ERR_CGI_PARAMS;
3199 /* At this point, the line to remove is in cur_line, and the previous
3200 * one is in prev_line
3203 /* Unlink cur_line */
3204 prev_line->next = cur_line->next;
3205 cur_line->next = NULL;
3208 edit_free_file_lines(cur_line);
3210 err = edit_write_file(file);
3213 /* Error writing file */
3214 edit_free_file(file);
3218 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3219 string_append(&target, file->identifier);
3221 edit_free_file(file);
3226 return JB_ERR_MEMORY;
3229 rsp->status = strdup("302 Local Redirect from Junkbuster");
3230 if (rsp->status == NULL)
3233 return JB_ERR_MEMORY;
3235 err = enlist_unique_header(rsp->headers, "Location", target);
3242 /*********************************************************************
3244 * Function : cgi_edit_actions_section_remove
3246 * Description : CGI function that actually removes a whole section from
3247 * the actions file. The section must be empty first
3248 * (else JB_ERR_CGI_PARAMS).
3251 * 1 : csp = Current client state (buffers, headers, etc...)
3252 * 2 : rsp = http_response data structure for output
3253 * 3 : parameters = map of cgi parameters
3256 * filename : Identifies the file to edit
3257 * ver : File's last-modified time
3258 * section : Line number of section to edit
3259 * pattern : Line number of pattern to edit
3261 * Returns : JB_ERR_OK on success
3262 * JB_ERR_MEMORY on out-of-memory
3263 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3264 * specified or not valid.
3266 *********************************************************************/
3267 jb_err cgi_edit_actions_section_remove(struct client_state *csp,
3268 struct http_response *rsp,
3269 const struct map *parameters)
3272 struct editable_file * file;
3273 struct file_line * cur_line;
3274 struct file_line * prev_line;
3275 unsigned line_number;
3279 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3281 return cgi_error_disabled(csp, rsp);
3284 err = get_number_param(csp, parameters, "section", §ionid);
3290 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3293 /* No filename specified, can't read file, modified, or out of memory. */
3294 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3298 cur_line = file->lines;
3301 while ((cur_line != NULL) && (line_number < sectionid))
3303 prev_line = cur_line;
3304 cur_line = cur_line->next;
3308 if ( (cur_line == NULL)
3309 || (cur_line->type != FILE_LINE_ACTION) )
3311 /* Invalid "sectionid" parameter */
3312 edit_free_file(file);
3313 return JB_ERR_CGI_PARAMS;
3316 if ( (cur_line->next != NULL)
3317 && (cur_line->next->type == FILE_LINE_URL) )
3319 /* Section not empty. */
3320 edit_free_file(file);
3321 return JB_ERR_CGI_PARAMS;
3324 /* At this point, the line to remove is in cur_line, and the previous
3325 * one is in prev_line
3328 /* Unlink cur_line */
3329 if (prev_line == NULL)
3331 /* Removing the first line from the file */
3332 file->lines = cur_line->next;
3336 prev_line->next = cur_line->next;
3338 cur_line->next = NULL;
3341 edit_free_file_lines(cur_line);
3343 err = edit_write_file(file);
3346 /* Error writing file */
3347 edit_free_file(file);
3351 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3352 string_append(&target, file->identifier);
3354 edit_free_file(file);
3359 return JB_ERR_MEMORY;
3362 rsp->status = strdup("302 Local Redirect from Junkbuster");
3363 if (rsp->status == NULL)
3366 return JB_ERR_MEMORY;
3368 err = enlist_unique_header(rsp->headers, "Location", target);
3375 /*********************************************************************
3377 * Function : cgi_edit_actions_section_add
3379 * Description : CGI function that adds a new empty section to
3383 * 1 : csp = Current client state (buffers, headers, etc...)
3384 * 2 : rsp = http_response data structure for output
3385 * 3 : parameters = map of cgi parameters
3388 * filename : Identifies the file to edit
3389 * ver : File's last-modified time
3390 * section : Line number of section to add after, 0 for start
3393 * Returns : JB_ERR_OK on success
3394 * JB_ERR_MEMORY on out-of-memory
3395 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3396 * specified or not valid.
3398 *********************************************************************/
3399 jb_err cgi_edit_actions_section_add(struct client_state *csp,
3400 struct http_response *rsp,
3401 const struct map *parameters)
3404 struct file_line * new_line;
3406 struct editable_file * file;
3407 struct file_line * cur_line;
3408 unsigned line_number;
3412 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3414 return cgi_error_disabled(csp, rsp);
3417 err = get_number_param(csp, parameters, "section", §ionid);
3423 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3426 /* No filename specified, can't read file, modified, or out of memory. */
3427 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3431 cur_line = file->lines;
3435 /* Add to start of file */
3436 if (cur_line != NULL)
3438 /* There's something in the file, find the line before the first
3441 while ( (cur_line->next != NULL)
3442 && (cur_line->next->type != FILE_LINE_ACTION) )
3444 cur_line = cur_line->next;
3451 /* Add after stated section. */
3452 while ((cur_line != NULL) && (line_number < sectionid))
3454 cur_line = cur_line->next;
3458 if ( (cur_line == NULL)
3459 || (cur_line->type != FILE_LINE_ACTION))
3461 /* Invalid "sectionid" parameter */
3462 edit_free_file(file);
3463 return JB_ERR_CGI_PARAMS;
3466 /* Skip through the section to find the last line in it. */
3467 while ( (cur_line->next != NULL)
3468 && (cur_line->next->type != FILE_LINE_ACTION) )
3470 cur_line = cur_line->next;
3475 /* At this point, the last line in the previous section is in cur_line
3476 * - add after this. (Or if we need to add as the first line, cur_line
3480 new_text = strdup("{}");
3481 if (NULL == new_text)
3483 edit_free_file(file);
3484 return JB_ERR_MEMORY;
3487 /* Allocate the new line */
3488 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3489 if (new_line == NULL)
3492 edit_free_file(file);
3493 return JB_ERR_MEMORY;
3496 /* Fill in the data members of the new line */
3497 new_line->raw = NULL;
3498 new_line->prefix = NULL;
3499 new_line->unprocessed = new_text;
3500 new_line->type = FILE_LINE_ACTION;
3502 if (cur_line != NULL)
3504 /* Link new_line into the list, after cur_line */
3505 new_line->next = cur_line->next;
3506 cur_line->next = new_line;
3510 /* Link new_line into the list, as first line */
3511 new_line->next = file->lines;
3512 file->lines = new_line;
3515 /* Done making changes, now commit */
3517 err = edit_write_file(file);
3520 /* Error writing file */
3521 edit_free_file(file);
3525 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3526 string_append(&target, file->identifier);
3528 edit_free_file(file);
3533 return JB_ERR_MEMORY;
3536 rsp->status = strdup("302 Local Redirect from Junkbuster");
3537 if (rsp->status == NULL)
3540 return JB_ERR_MEMORY;
3542 err = enlist_unique_header(rsp->headers, "Location", target);
3549 /*********************************************************************
3551 * Function : cgi_toggle
3553 * Description : CGI function that adds a new empty section to
3557 * 1 : csp = Current client state (buffers, headers, etc...)
3558 * 2 : rsp = http_response data structure for output
3559 * 3 : parameters = map of cgi parameters
3562 * set : If present, how to change toggle setting:
3563 * "enable", "disable", "toggle", or none (default).
3564 * mini : If present, use mini reply template.
3566 * Returns : JB_ERR_OK on success
3567 * JB_ERR_MEMORY on out-of-memory
3569 *********************************************************************/
3570 jb_err cgi_toggle(struct client_state *csp,
3571 struct http_response *rsp,
3572 const struct map *parameters)
3574 struct map *exports;
3576 const char *template_name;
3583 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE))
3585 return cgi_error_disabled(csp, rsp);
3588 if (NULL == (exports = default_exports(csp, "toggle")))
3590 return JB_ERR_MEMORY;
3593 mode = *(lookup(parameters, "set"));
3600 else if (mode == 'd')
3605 else if (mode == 't')
3608 g_bToggleIJB = !g_bToggleIJB;
3611 err = map_conditional(exports, "enabled", g_bToggleIJB);
3618 template_name = (*(lookup(parameters, "mini"))
3622 return template_fill_for_cgi(csp, template_name, exports, rsp);
3624 #endif /* def FEATURE_CGI_EDIT_ACTIONS */