1 const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.3 2001/10/14 22:12:49 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.3 2001/10/14 22:12:49 jongfoster
39 * New version of CGI-based actionsfile editor.
40 * Major changes, including:
41 * - Completely new file parser and file output routines
42 * - edit-actions CGI renamed edit-actions-for-url
43 * - All CGIs now need a filename parameter, except for...
44 * - New CGI edit-actions which doesn't need a filename,
45 * to allow you to start the editor up.
46 * - edit-actions-submit now works, and now automatically
47 * redirects you back to the main edit-actions-list handler.
49 * Revision 1.2 2001/09/16 17:05:14 jongfoster
50 * Removing unused #include showarg.h
52 * Revision 1.1 2001/09/16 15:47:37 jongfoster
53 * First version of CGI-based edit interface. This is very much a
54 * work-in-progress, and you can't actually use it to edit anything
55 * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes
59 **********************************************************************/
65 * FIXME: Following includes copied from cgi.c - which are actually needed?
70 #include <sys/types.h>
79 #define snprintf _snprintf
80 #endif /* def _WIN32 */
85 #include "cgisimple.h"
92 /* loadcfg.h is for g_bToggleIJB only */
94 const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION;
97 #ifdef FEATURE_CGI_EDIT_ACTIONS
101 struct file_line * next;
109 struct action_spec action[1];
118 /* Add more data types here... e.g.
121 struct url_spec url[1];
125 struct action_spec action[1];
134 #define FILE_LINE_UNPROCESSED 1
135 #define FILE_LINE_BLANK 2
136 #define FILE_LINE_ALIAS_HEADER 3
137 #define FILE_LINE_ALIAS_ENTRY 4
138 #define FILE_LINE_ACTION 5
139 #define FILE_LINE_URL 6
140 #define FILE_LINE_SETTINGS_HEADER 7
141 #define FILE_LINE_SETTINGS_ENTRY 8
142 #define FILE_LINE_DESCRIPTION_HEADER 9
143 #define FILE_LINE_DESCRIPTION_ENTRY 10
148 struct file_line * lines;
149 const char * filename; /* Full pathname - e.g. "/etc/junkbuster/wibble.action" */
150 const char * identifier; /* Filename stub - e.g. "wibble". Use for CGI param. */
151 const char * version_str; /* Last modification time, as a string. For CGI param */
152 unsigned version; /* Last modification time - prevents chaos with
153 * the browser's "back" button. Note that this is a
154 * time_t cast to an unsigned. When comparing, always
155 * cast the time_t to an unsigned, and *NOT* vice-versa.
156 * This may lose the top few bits, but they're not
157 * significant anyway.
159 struct file_line * parse_error; /* On parse error, this is the offending line. */
160 const char * parse_error_text; /* On parse error, this is the problem.
161 * (Statically allocated) */
164 /* FIXME: Following non-static functions should be prototyped in .h or made static */
166 /* Functions to read and write arbitrary config files */
167 jb_err edit_read_file(struct client_state *csp,
168 const struct map *parameters,
171 struct editable_file **pfile);
172 jb_err edit_write_file(struct editable_file * file);
173 void edit_free_file(struct editable_file * file);
175 /* Functions to read and write actions files */
176 jb_err edit_parse_actions_file(struct editable_file * file);
177 jb_err edit_read_actions_file(struct client_state *csp,
178 struct http_response *rsp,
179 const struct map *parameters,
181 struct editable_file **pfile);
184 jb_err cgi_error_modified(struct client_state *csp,
185 struct http_response *rsp,
186 const char *filename);
187 jb_err cgi_error_parse(struct client_state *csp,
188 struct http_response *rsp,
189 struct editable_file *file);
190 jb_err cgi_error_file(struct client_state *csp,
191 struct http_response *rsp,
192 const char *filename);
194 /* Internal arbitrary config file support functions */
195 static jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile);
196 static void edit_free_file_lines(struct file_line * first_line);
197 static jb_err simple_read_line(char **dest, FILE *fp);
198 static jb_err edit_read_line(FILE *fp, char **raw_out, char **prefix_out, char **data_out);
200 /* Internal actions file support functions */
201 static int match_actions_file_header_line(const char * line, const char * name);
202 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue);
204 /* Internal parameter parsing functions */
205 static jb_err get_file_name_param(struct client_state *csp,
206 const struct map *parameters,
207 const char *param_name,
210 const char **pparam);
211 static jb_err get_number_param(struct client_state *csp,
212 const struct map *parameters,
216 /* Internal actionsfile <==> HTML conversion functions */
217 static jb_err map_radio(struct map * exports,
218 const char * optionname,
221 static jb_err actions_to_radio(struct map * exports,
222 const struct action_spec *action);
223 static jb_err actions_from_radio(const struct map * parameters,
224 struct action_spec *action);
227 /*********************************************************************
229 * Function : simple_read_line
231 * Description : Read a single line from a file and return it.
232 * This is basically a version of fgets() that malloc()s
233 * it's own line buffer. Note that the buffer will
234 * always be a multiple of BUFFER_SIZE bytes long.
235 * Therefore if you are going to keep the string for
236 * an extended period of time, you should probably
237 * strdup() it and free() the original, to save memory.
241 * 1 : dest = destination for newly malloc'd pointer to
242 * line data. Will be set to NULL on error.
243 * 2 : fp = File to read from
245 * Returns : JB_ERR_OK on success
246 * JB_ERR_MEMORY on out-of-memory
247 * JB_ERR_FILE on EOF.
249 *********************************************************************/
250 static jb_err simple_read_line(char **dest, FILE *fp)
261 if (NULL == (buf = malloc(BUFFER_SIZE)))
263 return JB_ERR_MEMORY;
272 if ((!fgets(newbuf, BUFFER_SIZE, fp)) || (*newbuf == '\0'))
274 /* (*newbuf == '\0') should never happen unless fgets fails */
287 if ((buf[len - 1] == '\n') || (buf[len - 1] == '\r'))
293 if (NULL == (newbuf = realloc(buf, len + BUFFER_SIZE)))
296 return JB_ERR_MEMORY;
303 /*********************************************************************
305 * Function : edit_read_line
307 * Description : Read a single non-empty line from a file and return
308 * it. Trims comments, leading and trailing whitespace
309 * and respects escaping of newline and comment char.
310 * Provides the line in 2 alternative forms: raw and
312 * - raw is the raw data read from the file. If the
313 * line is not modified, then this should be written
315 * - prefix is any comments and blank lines that were
316 * read from the file. If the line is modified, then
317 * this should be written out to the file followed
318 * by the modified data. (If this string is non-empty
319 * then it will have a newline at the end).
320 * - data is the actual data that will be parsed
321 * further by appropriate routines.
322 * On EOF, the 3 strings will all be set to NULL and
323 * 0 will be returned.
326 * 1 : fp = File to read from
327 * 2 : raw_out = destination for newly malloc'd pointer to
328 * raw line data. May be NULL if you don't want it.
329 * 3 : prefix_out = destination for newly malloc'd pointer to
330 * comments. May be NULL if you don't want it.
331 * 4 : data_out = destination for newly malloc'd pointer to
332 * line data with comments and leading/trailing spaces
333 * removed, and line continuation performed. May be
334 * NULL if you don't want it.
336 * Returns : JB_ERR_OK on success
337 * JB_ERR_MEMORY on out-of-memory
338 * JB_ERR_FILE on EOF.
340 *********************************************************************/
341 static jb_err edit_read_line(FILE *fp, char **raw_out, char **prefix_out, char **data_out)
343 char *p; /* Temporary pointer */
344 char *linebuf; /* Line read from file */
345 char *linestart; /* Start of linebuf, usually first non-whitespace char */
346 char newline[3]; /* Used to store the newline - "\n", "\r", or "\r\n" */
347 int contflag = 0; /* Nonzero for line continuation - i.e. line ends '\' */
348 char *raw; /* String to be stored in raw_out */
349 char *prefix; /* String to be stored in prefix_out */
350 char *data; /* String to be stored in data_out */
351 jb_err rval = JB_ERR_OK;
355 /* Set output parameters to NULL */
369 /* Set string variables to new, empty strings. */
375 if ((raw == NULL) || (prefix == NULL) || (data == NULL))
380 return JB_ERR_MEMORY;
387 /* Main loop. Loop while we need more data & it's not EOF. */
389 while ( (contflag || (*data == '\0'))
390 && (JB_ERR_OK == (rval = simple_read_line(&linebuf, fp))))
392 if (string_append(&raw,linebuf))
397 return JB_ERR_MEMORY;
400 /* Trim off newline */
401 p = linebuf + strlen(linebuf);
402 if ((p != linebuf) && ((p[-1] == '\r') || (p[-1] == '\n')))
405 if ((p != linebuf) && ((p[-1] == '\r') || (p[-1] == '\n')))
413 /* Line continuation? Trim escape and set flag. */
414 contflag = ((p != linebuf) && (*--p == '\\'));
420 /* Trim leading spaces if we're at the start of the line */
424 /* Trim leading spaces */
425 while (*linestart && isspace((int)(unsigned char)*linestart))
431 /* Handle comment characters. */
433 while ((p = strchr(p, '#')) != NULL)
435 /* Found a comment char.. */
436 if ((p != linebuf) && (*(p-1) == '\\'))
438 /* ..and it's escaped, left-shift the line over the escape. */
440 while ((*q = *(q + 1)) != '\0')
444 /* Now scan from just after the "#". */
448 /* Real comment. Save it... */
451 /* Special case: Line only contains a comment, so all the
452 * previous whitespace is considered part of the comment.
453 * Undo the whitespace skipping, if any.
458 string_append(&prefix,p);
459 if (string_append(&prefix,newline))
464 return JB_ERR_MEMORY;
468 /* ... and chop off the rest of the line */
471 } /* END while (there's a # character) */
473 /* Write to the buffer */
476 if (string_append(&data, linestart))
481 return JB_ERR_MEMORY;
486 } /* END while(we need more data) */
488 /* Handle simple_read_line() errors - ignore EOF */
489 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
500 /* Got at least some data */
502 /* Remove trailing whitespace */
515 *prefix_out = prefix;
533 /* EOF and no data there. */
544 /*********************************************************************
546 * Function : edit_write_file
548 * Description : Write a complete file to disk.
551 * 1 : filename = File to write to.
552 * 2 : file = Data structure to write.
554 * Returns : JB_ERR_OK on success
555 * JB_ERR_FILE on error writing to file.
556 * JB_ERR_MEMORY on out of memory
558 *********************************************************************/
559 jb_err edit_write_file(struct editable_file * file)
562 struct file_line * cur_line;
563 struct stat statbuf[1];
564 char version_buf[22]; /* 22 = ceil(log10(2^64)) + 2 = max number of
565 digits in time_t, assuming this is a 64-bit
566 machine, plus null terminator, plus one
570 assert(file->filename);
572 if (NULL == (fp = fopen(file->filename, "wt")))
577 cur_line = file->lines;
578 while (cur_line != NULL)
582 if (fputs(cur_line->raw, fp) < 0)
590 if (cur_line->prefix)
592 if (fputs(cur_line->prefix, fp) < 0)
598 if (cur_line->unprocessed)
600 if (fputs(cur_line->unprocessed, fp) < 0)
605 if (fputs("\n", fp) < 0)
613 /* FIXME: Write data from file->data->whatever */
617 cur_line = cur_line->next;
623 /* Update the version stamp in the file structure, since we just
624 * wrote to the file & changed it's date.
626 if (stat(file->filename, statbuf) < 0)
628 /* Error, probably file not found. */
631 file->version = (unsigned)statbuf->st_mtime;
633 /* Correct file->version_str */
634 freez((char *)file->version_str);
635 snprintf(version_buf, 22, "%u", file->version);
636 version_buf[21] = '\0';
637 file->version_str = strdup(version_buf);
638 if (version_buf == NULL)
640 return JB_ERR_MEMORY;
647 /*********************************************************************
649 * Function : edit_free_file
651 * Description : Free a complete file in memory.
654 * 1 : file = Data structure to free.
658 *********************************************************************/
659 void edit_free_file(struct editable_file * file)
663 /* Silently ignore NULL pointer */
667 edit_free_file_lines(file->lines);
668 freez((char *)file->filename);
669 freez((char *)file->identifier);
670 freez((char *)file->version_str);
672 file->parse_error_text = NULL; /* Statically allocated */
673 file->parse_error = NULL;
679 /*********************************************************************
681 * Function : edit_free_file
683 * Description : Free an entire linked list of file lines.
686 * 1 : first_line = Data structure to free.
690 *********************************************************************/
691 static void edit_free_file_lines(struct file_line * first_line)
693 struct file_line * next_line;
695 while (first_line != NULL)
697 next_line = first_line->next;
698 first_line->next = NULL;
699 freez(first_line->raw);
700 freez(first_line->prefix);
701 freez(first_line->unprocessed);
702 switch(first_line->type)
704 case 0: /* special case if memory zeroed */
705 case FILE_LINE_UNPROCESSED:
706 case FILE_LINE_BLANK:
707 case FILE_LINE_ALIAS_HEADER:
708 case FILE_LINE_SETTINGS_HEADER:
709 case FILE_LINE_DESCRIPTION_HEADER:
710 case FILE_LINE_DESCRIPTION_ENTRY:
711 case FILE_LINE_ALIAS_ENTRY:
713 /* No data is stored for these */
716 case FILE_LINE_ACTION:
717 free_action(first_line->data.action);
720 case FILE_LINE_SETTINGS_ENTRY:
721 freez(first_line->data.setting.name);
722 freez(first_line->data.setting.svalue);
725 /* Should never happen */
729 first_line->type = 0; /* paranoia */
731 first_line = next_line;
736 /*********************************************************************
738 * Function : match_actions_file_header_line
740 * Description : Match an actions file {{header}} line
743 * 1 : line - String from file
744 * 2 : name - Header to match against
746 * Returns : 0 iff they match.
748 *********************************************************************/
749 static int match_actions_file_header_line(const char * line, const char * name)
757 if ((line[0] != '{') || (line[1] != '{'))
763 /* Look for optional whitespace */
764 while ( (*line == ' ') || (*line == '\t') )
769 /* Look for the specified name (case-insensitive) */
771 if (0 != strncmpic(line, name, len))
777 /* Look for optional whitespace */
778 while ( (*line == ' ') || (*line == '\t') )
783 /* Look for "}}" and end of string*/
784 if ((line[0] != '}') || (line[1] != '}') || (line[2] != '\0'))
794 /*********************************************************************
796 * Function : match_actions_file_header_line
798 * Description : Match an actions file {{header}} line
801 * 1 : line - String from file. Must not start with
802 * whitespace (else infinite loop!)
803 * 2 : name - Destination for name
804 * 2 : name - Destination for value
806 * Returns : JB_ERR_OK on success
807 * JB_ERR_MEMORY on out-of-memory
808 * JB_ERR_PARSE if there's no "=" sign, or if there's
809 * nothing before the "=" sign (but empty
810 * values *after* the "=" sign are legal).
812 *********************************************************************/
813 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue)
815 const char * name_end;
816 const char * value_start;
822 assert(*line != ' ');
823 assert(*line != '\t');
828 value_start = strchr(line, '=');
829 if ((value_start == NULL) || (value_start == line))
834 name_end = value_start - 1;
836 /* Eat any whitespace before the '=' */
837 while ((*name_end == ' ') || (*name_end == '\t'))
840 * we already know we must have at least 1 non-ws char
841 * at start of buf - no need to check
846 name_len = name_end - line + 1; /* Length excluding \0 */
847 if (NULL == (*pname = (char *) malloc(name_len + 1)))
849 return JB_ERR_MEMORY;
851 strncpy(*pname, line, name_len);
852 (*pname)[name_len] = '\0';
854 /* Eat any the whitespace after the '=' */
856 while ((*value_start == ' ') || (*value_start == '\t'))
861 if (NULL == (*pvalue = strdup(value_start)))
865 return JB_ERR_MEMORY;
872 /*********************************************************************
874 * Function : edit_parse_actions_file
876 * Description : Parse an actions file in memory.
878 * Passed linked list must have the "data" member
879 * zeroed, and must contain valid "next" and
880 * "unprocessed" fields. The "raw" and "prefix"
881 * fields are ignored, and "type" is just overwritten.
883 * Note that on error the file may have been
887 * 1 : file = Actions file to be parsed in-place.
889 * Returns : JB_ERR_OK on success
890 * JB_ERR_MEMORY on out-of-memory
891 * JB_ERR_PARSE on error
893 *********************************************************************/
894 jb_err edit_parse_actions_file(struct editable_file * file)
896 struct file_line * cur_line;
898 const char * text; /* Text from a line */
899 char * name; /* For lines of the form name=value */
900 char * value; /* For lines of the form name=value */
901 struct action_alias * alias_list = NULL;
902 jb_err err = JB_ERR_OK;
904 /* alias_list contains the aliases defined in this file.
905 * It might be better to use the "file_line.data" fields
906 * in the relavent places instead.
909 cur_line = file->lines;
911 /* A note about blank line support: Blank lines should only
912 * ever occur as the last line in the file. This function
913 * is more forgiving than that - FILE_LINE_BLANK can occur
917 /* Skip leading blanks. Should only happen if file is
918 * empty (which is valid, but pointless).
920 while ( (cur_line != NULL)
921 && (cur_line->unprocessed[0] == '\0') )
924 cur_line->type = FILE_LINE_BLANK;
925 cur_line = cur_line->next;
928 if ( (cur_line != NULL)
929 && (cur_line->unprocessed[0] != '{') )
931 /* File doesn't start with a header */
932 file->parse_error = cur_line;
933 file->parse_error_text = "First (non-comment) line of the file must contain a header.";
937 if ( (cur_line != NULL) && (0 ==
938 match_actions_file_header_line(cur_line->unprocessed, "settings") ) )
940 cur_line->type = FILE_LINE_SETTINGS_HEADER;
942 cur_line = cur_line->next;
943 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
945 if (cur_line->unprocessed[0])
947 cur_line->type = FILE_LINE_SETTINGS_ENTRY;
949 err = split_line_on_equals(cur_line->unprocessed,
950 &cur_line->data.setting.name,
951 &cur_line->data.setting.svalue);
952 if (err == JB_ERR_MEMORY)
956 else if (err != JB_ERR_OK)
958 /* Line does not contain a name=value pair */
959 file->parse_error = cur_line;
960 file->parse_error_text = "Expected a name=value pair on this {{description}} line, but couldn't find one.";
966 cur_line->type = FILE_LINE_BLANK;
968 cur_line = cur_line->next;
972 if ( (cur_line != NULL) && (0 ==
973 match_actions_file_header_line(cur_line->unprocessed, "description") ) )
975 cur_line->type = FILE_LINE_DESCRIPTION_HEADER;
977 cur_line = cur_line->next;
978 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
980 if (cur_line->unprocessed[0])
982 cur_line->type = FILE_LINE_DESCRIPTION_ENTRY;
986 cur_line->type = FILE_LINE_BLANK;
988 cur_line = cur_line->next;
992 if ( (cur_line != NULL) && (0 ==
993 match_actions_file_header_line(cur_line->unprocessed, "alias") ) )
995 cur_line->type = FILE_LINE_ALIAS_HEADER;
997 cur_line = cur_line->next;
998 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1000 if (cur_line->unprocessed[0])
1002 /* define an alias */
1003 struct action_alias * new_alias;
1005 cur_line->type = FILE_LINE_ALIAS_ENTRY;
1007 err = split_line_on_equals(cur_line->unprocessed, &name, &value);
1008 if (err == JB_ERR_MEMORY)
1012 else if (err != JB_ERR_OK)
1014 /* Line does not contain a name=value pair */
1015 file->parse_error = cur_line;
1016 file->parse_error_text = "Expected a name=value pair on this {{alias}} line, but couldn't find one.";
1017 return JB_ERR_PARSE;
1020 if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1025 free_alias_list(alias_list);
1026 return JB_ERR_MEMORY;
1029 err = get_actions(value, alias_list, new_alias->action);
1032 /* Invalid action or out of memory */
1036 free_alias_list(alias_list);
1037 if (err == JB_ERR_MEMORY)
1043 /* Line does not contain a name=value pair */
1044 file->parse_error = cur_line;
1045 file->parse_error_text = "This alias does not specify a valid set of actions.";
1046 return JB_ERR_PARSE;
1052 new_alias->name = name;
1055 new_alias->next = alias_list;
1056 alias_list = new_alias;
1060 cur_line->type = FILE_LINE_BLANK;
1062 cur_line = cur_line->next;
1066 /* Header done, process the main part of the file */
1067 while (cur_line != NULL)
1069 /* At this point, (cur_line->unprocessed[0] == '{') */
1070 assert(cur_line->unprocessed[0] == '{');
1071 text = cur_line->unprocessed + 1;
1072 len = strlen(text) - 1;
1073 if (text[len] != '}')
1075 /* No closing } on header */
1076 free_alias_list(alias_list);
1077 file->parse_error = cur_line;
1078 file->parse_error_text = "Headers starting with '{' must have a "
1079 "closing bracket ('}'). Headers starting with two brackets ('{{') "
1080 "must close with two brackets ('}}').";
1081 return JB_ERR_PARSE;
1086 /* An invalid {{ header. */
1087 free_alias_list(alias_list);
1088 file->parse_error = cur_line;
1089 file->parse_error_text = "Unknown or unexpected two-bracket header. "
1090 "Please remember that the system (two-bracket) headers must "
1091 "appear in the order {{settings}}, {{description}}, {{alias}}, "
1092 "and must appear before any actions (one-bracket) headers. "
1093 "Also note that system headers may not be repeated.";
1094 return JB_ERR_PARSE;
1097 while ( (*text == ' ') || (*text == '\t') )
1103 && ( (text[len - 1] == ' ')
1104 || (text[len - 1] == '\t') ) )
1109 cur_line->type = FILE_LINE_ACTION;
1111 /* Remove {} and make copy */
1112 if (NULL == (value = (char *) malloc(len + 1)))
1115 free_alias_list(alias_list);
1116 return JB_ERR_MEMORY;
1118 strncpy(value, text, len);
1122 err = get_actions(value, alias_list, cur_line->data.action);
1125 /* Invalid action or out of memory */
1127 free_alias_list(alias_list);
1128 if (err == JB_ERR_MEMORY)
1134 /* Line does not contain a name=value pair */
1135 file->parse_error = cur_line;
1136 file->parse_error_text = "This header does not specify a valid set of actions.";
1137 return JB_ERR_PARSE;
1141 /* Done with string - it was clobbered anyway */
1144 /* Process next line */
1145 cur_line = cur_line->next;
1147 /* Loop processing URL patterns */
1148 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1150 if (cur_line->unprocessed[0])
1152 /* Could parse URL here, but this isn't currently needed */
1154 cur_line->type = FILE_LINE_URL;
1158 cur_line->type = FILE_LINE_BLANK;
1160 cur_line = cur_line->next;
1162 } /* End main while(cur_line != NULL) loop */
1164 free_alias_list(alias_list);
1170 /*********************************************************************
1172 * Function : edit_read_file_lines
1174 * Description : Read all the lines of a file into memory.
1175 * Handles whitespace, comments and line continuation.
1178 * 1 : fp = File to read from. On return, this will be
1179 * at EOF but it will not have been closed.
1180 * 2 : pfile = Destination for a linked list of file_lines.
1181 * Will be set to NULL on error.
1183 * Returns : JB_ERR_OK on success
1184 * JB_ERR_MEMORY on out-of-memory
1186 *********************************************************************/
1187 jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile)
1189 struct file_line * first_line; /* Keep for return value or to free */
1190 struct file_line * cur_line; /* Current line */
1191 struct file_line * prev_line; /* Entry with prev_line->next = cur_line */
1199 cur_line = first_line = zalloc(sizeof(struct file_line));
1200 if (cur_line == NULL)
1202 return JB_ERR_MEMORY;
1205 cur_line->type = FILE_LINE_UNPROCESSED;
1207 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed);
1210 /* Out of memory or empty file. */
1211 /* Note that empty file is not an error we propogate up */
1213 return ((rval == JB_ERR_FILE) ? JB_ERR_OK : rval);
1218 prev_line = cur_line;
1219 cur_line = prev_line->next = zalloc(sizeof(struct file_line));
1220 if (cur_line == NULL)
1223 edit_free_file_lines(first_line);
1224 return JB_ERR_MEMORY;
1227 cur_line->type = FILE_LINE_UNPROCESSED;
1229 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed);
1230 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
1233 edit_free_file_lines(first_line);
1234 return JB_ERR_MEMORY;
1238 while (rval != JB_ERR_FILE);
1242 /* We allocated one too many - free it */
1243 prev_line->next = NULL;
1246 *pfile = first_line;
1251 /*********************************************************************
1253 * Function : edit_read_file
1255 * Description : Read a complete file into memory.
1256 * Handles CGI parameter parsing. If requested, also
1257 * checks the file's modification timestamp.
1260 * 1 : csp = Current client state (buffers, headers, etc...)
1261 * 2 : parameters = map of cgi parameters.
1262 * 3 : require_version = true to check "ver" parameter.
1263 * 4 : suffix = File extension, e.g. ".action".
1264 * 5 : pfile = Destination for the file. Will be set
1268 * filename : The name of the file to read, without the
1269 * path or ".action" extension.
1270 * ver : (Only if require_version is nonzero)
1271 * Timestamp of the actions file. If wrong, this
1272 * function fails with JB_ERR_MODIFIED.
1274 * Returns : JB_ERR_OK on success
1275 * JB_ERR_MEMORY on out-of-memory
1276 * JB_ERR_CGI_PARAMS if "filename" was not specified
1278 * JB_ERR_FILE if the file cannot be opened or
1280 * JB_ERR_MODIFIED if version checking was requested and
1281 * failed - the file was modified outside
1282 * of this CGI editor instance.
1284 *********************************************************************/
1285 jb_err edit_read_file(struct client_state *csp,
1286 const struct map *parameters,
1287 int require_version,
1289 struct editable_file **pfile)
1291 struct file_line * lines;
1295 const char * identifier;
1296 struct editable_file * file;
1297 unsigned version = 0;
1298 struct stat statbuf[1];
1299 char version_buf[22];
1307 err = get_file_name_param(csp, parameters, "filename", suffix,
1308 &filename, &identifier);
1314 if (stat(filename, statbuf) < 0)
1316 /* Error, probably file not found. */
1320 version = (unsigned) statbuf->st_mtime;
1322 if (require_version)
1324 unsigned specified_version;
1325 err = get_number_param(csp, parameters, "ver", &specified_version);
1332 if (version != specified_version)
1334 return JB_ERR_MODIFIED;
1338 if (NULL == (fp = fopen(filename,"rt")))
1344 err = edit_read_file_lines(fp, &lines);
1354 file = (struct editable_file *) zalloc(sizeof(*file));
1358 edit_free_file_lines(lines);
1362 file->lines = lines;
1363 file->filename = filename;
1364 file->version = version;
1365 file->identifier = strdup(identifier);
1367 if (file->identifier == NULL)
1369 edit_free_file(file);
1370 return JB_ERR_MEMORY;
1373 /* Correct file->version_str */
1374 freez((char *)file->version_str);
1375 snprintf(version_buf, 22, "%u", file->version);
1376 version_buf[21] = '\0';
1377 file->version_str = strdup(version_buf);
1378 if (version_buf == NULL)
1380 edit_free_file(file);
1381 return JB_ERR_MEMORY;
1389 /*********************************************************************
1391 * Function : edit_read_actions_file
1393 * Description : Read a complete actions file into memory.
1394 * Handles CGI parameter parsing. If requested, also
1395 * checks the file's modification timestamp.
1397 * If this function detects an error in the categories
1398 * JB_ERR_FILE, JB_ERR_MODIFIED, or JB_ERR_PARSE,
1399 * then it handles it by filling in the specified
1400 * response structure and returning JB_ERR_FILE.
1403 * 1 : csp = Current client state (buffers, headers, etc...)
1404 * 2 : rsp = HTTP response. Only filled in on error.
1405 * 2 : parameters = map of cgi parameters.
1406 * 3 : require_version = true to check "ver" parameter.
1407 * 4 : pfile = Destination for the file. Will be set
1411 * filename : The name of the actions file to read, without the
1412 * path or ".action" extension.
1413 * ver : (Only if require_version is nonzero)
1414 * Timestamp of the actions file. If wrong, this
1415 * function fails with JB_ERR_MODIFIED.
1417 * Returns : JB_ERR_OK on success
1418 * JB_ERR_MEMORY on out-of-memory
1419 * JB_ERR_CGI_PARAMS if "filename" was not specified
1421 * JB_ERR_FILE if the file does not contain valid data,
1422 * or if file cannot be opened or
1423 * contains no data, or if version
1424 * checking was requested and failed.
1426 *********************************************************************/
1427 jb_err edit_read_actions_file(struct client_state *csp,
1428 struct http_response *rsp,
1429 const struct map *parameters,
1430 int require_version,
1431 struct editable_file **pfile)
1434 struct editable_file *file;
1442 err = edit_read_file(csp, parameters, require_version, ".action", &file);
1445 /* Try to handle if possible */
1446 if (err == JB_ERR_FILE)
1448 err = cgi_error_file(csp, rsp, lookup(parameters, "filename"));
1450 else if (err == JB_ERR_MODIFIED)
1452 err = cgi_error_modified(csp, rsp, lookup(parameters, "filename"));
1454 if (err == JB_ERR_OK)
1457 * Signal to higher-level CGI code that there was a problem but we
1458 * handled it, they should just return JB_ERR_OK.
1465 err = edit_parse_actions_file(file);
1468 if (err == JB_ERR_PARSE)
1470 err = cgi_error_parse(csp, rsp, file);
1471 if (err == JB_ERR_OK)
1474 * Signal to higher-level CGI code that there was a problem but we
1475 * handled it, they should just return JB_ERR_OK.
1480 edit_free_file(file);
1489 /*********************************************************************
1491 * Function : get_file_name_param
1493 * Description : Get the name of the file to edit from the parameters
1494 * passed to a CGI function. This function handles
1495 * security checks such as blocking urls containing
1496 * "/" or ".", prepending the config file directory,
1497 * and adding the specified suffix.
1499 * (This is an essential security check, otherwise
1500 * users may be able to pass "../../../etc/passwd"
1501 * and overwrite the password file [linux], "prn:"
1502 * and print random data [Windows], etc...)
1504 * This function only allows filenames contining the
1505 * characters '-', '_', 'A'-'Z', 'a'-'z', and '0'-'9'.
1506 * That's probably too restrictive but at least it's
1510 * 1 : csp = Current client state (buffers, headers, etc...)
1511 * 2 : parameters = map of cgi parameters
1512 * 3 : suffix = File extension, e.g. ".actions"
1513 * 4 : pfilename = destination for full filename. Caller
1514 * free()s. Set to NULL on error.
1515 * 5 : pparam = destination for partial filename,
1516 * suitable for use in another URL. Allocated as part
1517 * of the map "parameters", so don't free it.
1518 * Set to NULL if not specified.
1520 * Returns : JB_ERR_OK on success
1521 * JB_ERR_MEMORY on out-of-memory
1522 * JB_ERR_CGI_PARAMS if "filename" was not specified
1525 *********************************************************************/
1526 static jb_err get_file_name_param(struct client_state *csp,
1527 const struct map *parameters,
1528 const char *param_name,
1531 const char **pparam)
1549 param = lookup(parameters, param_name);
1552 return JB_ERR_CGI_PARAMS;
1557 len = strlen(param);
1558 if (len >= FILENAME_MAX)
1561 return JB_ERR_CGI_PARAMS;
1564 /* Check every character to see if it's legal */
1566 while ((ch = *s++) != '\0')
1568 if ( ((ch < 'A') || (ch > 'Z'))
1569 && ((ch < 'a') || (ch > 'z'))
1570 && ((ch < '0') || (ch > '9'))
1574 /* Probable hack attempt. */
1575 return JB_ERR_CGI_PARAMS;
1579 /* Append extension */
1580 name = malloc(len + strlen(suffix) + 1);
1583 return JB_ERR_MEMORY;
1585 strcpy(name, param);
1586 strcpy(name + len, suffix);
1589 fullpath = make_path(csp->config->confdir, name);
1591 if (fullpath == NULL)
1593 return JB_ERR_MEMORY;
1597 *pfilename = fullpath;
1603 /*********************************************************************
1605 * Function : get_number_param
1607 * Description : Get a non-negative integer from the parameters
1608 * passed to a CGI function.
1611 * 1 : csp = Current client state (buffers, headers, etc...)
1612 * 2 : parameters = map of cgi parameters
1613 * 3 : name = Name of CGI parameter to read
1614 * 4 : pvalue = destination for value.
1615 * Set to -1 on error.
1617 * Returns : JB_ERR_OK on success
1618 * JB_ERR_MEMORY on out-of-memory
1619 * JB_ERR_CGI_PARAMS if the parameter was not specified
1622 *********************************************************************/
1623 static jb_err get_number_param(struct client_state *csp,
1624 const struct map *parameters,
1639 param = lookup(parameters, name);
1642 return JB_ERR_CGI_PARAMS;
1645 /* We don't use atoi because I want to check this carefully... */
1648 while ((ch = *param++) != '\0')
1650 if ((ch < '0') || (ch > '9'))
1652 return JB_ERR_CGI_PARAMS;
1659 * <limits.h> defines UINT_MAX
1661 * (UINT_MAX - ch) / 10 is the largest number that
1662 * can be safely multiplied by 10 then have ch added.
1664 if (value > ((UINT_MAX - (unsigned)ch) / 10U))
1666 return JB_ERR_CGI_PARAMS;
1669 value = value * 10 + ch;
1679 /*********************************************************************
1681 * Function : map_radio
1683 * Description : Map a set of radio button values. E.g. if you have
1684 * 3 radio buttons, declare them as:
1685 * <option type="radio" name="xyz" @xyz-a@>
1686 * <option type="radio" name="xyz" @xyz-b@>
1687 * <option type="radio" name="xyz" @xyz-c@>
1688 * Then map one of the @xyz-?@ variables to "checked"
1689 * and all the others to empty by calling:
1690 * map_radio(exports, "xyz", "abc", sel)
1691 * Where 'sel' is 'a', 'b', or 'c'.
1694 * 1 : exports = Exports map to modify.
1695 * 2 : optionname = name for map
1696 * 3 : values = null-terminated list of values;
1697 * 4 : value = Selected value.
1699 * CGI Parameters : None
1701 * Returns : JB_ERR_OK on success
1702 * JB_ERR_MEMORY on out-of-memory
1704 *********************************************************************/
1705 static jb_err map_radio(struct map * exports,
1706 const char * optionname,
1707 const char * values,
1719 len = strlen(optionname);
1720 buf = malloc(len + 3);
1723 return JB_ERR_MEMORY;
1726 strcpy(buf, optionname);
1731 while ((c = *values++) != '\0')
1736 if (map(exports, buf, 1, "", 1))
1739 return JB_ERR_MEMORY;
1745 if (map(exports, buf, 0, "checked", 1))
1748 return JB_ERR_MEMORY;
1755 /*********************************************************************
1757 * Function : actions_to_radio
1759 * Description : Converts a actionsfile entry into settings for
1760 * radio buttons and edit boxes on a HTML form.
1763 * 1 : exports = List of substitutions to add to.
1764 * 2 : action = Action to read
1766 * Returns : JB_ERR_OK on success
1767 * JB_ERR_MEMORY on out-of-memory
1769 *********************************************************************/
1770 static jb_err actions_to_radio(struct map * exports,
1771 const struct action_spec *action)
1773 unsigned mask = action->mask;
1774 unsigned add = action->add;
1782 mask = action->mask;
1785 /* sanity - prevents "-feature +feature" */
1789 #define DEFINE_ACTION_BOOL(name, bit) \
1790 if (!(mask & bit)) \
1792 current_mode = 'n'; \
1794 else if (add & bit) \
1796 current_mode = 'y'; \
1800 current_mode = 'x'; \
1802 if (map_radio(exports, name, "ynx", current_mode)) \
1804 return JB_ERR_MEMORY; \
1807 #define DEFINE_ACTION_STRING(name, bit, index) \
1808 DEFINE_ACTION_BOOL(name, bit); \
1811 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default) \
1814 checked = !strcmp(action->string[index], value); \
1818 checked = is_default; \
1820 mapped_param |= checked; \
1821 if (map(exports, name "-param-" value, 1, (checked ? "checked" : ""), 1)) \
1823 return JB_ERR_MEMORY; \
1826 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val) \
1827 if (map(exports, name "-param-custom", 1, \
1828 ((!mapped_param) ? "checked" : ""), 1)) \
1830 return JB_ERR_MEMORY; \
1832 if (map(exports, name "-param", 1, \
1833 (((add & bit) && !mapped_param) ? \
1834 action->string[index] : default_val), 1)) \
1836 return JB_ERR_MEMORY; \
1839 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val) \
1840 if (map(exports, name "-param", 1, \
1841 ((add & bit) ? action->string[index] : default_val), 1)) \
1843 return JB_ERR_MEMORY; \
1846 #define DEFINE_ACTION_MULTI(name, index) \
1847 if (action->multi_add[index]->first) \
1849 current_mode = 'y'; \
1851 else if (action->multi_remove_all[index]) \
1853 current_mode = 'n'; \
1855 else if (action->multi_remove[index]->first) \
1857 current_mode = 'y'; \
1861 current_mode = 'x'; \
1863 if (map_radio(exports, name, "ynx", current_mode)) \
1865 return JB_ERR_MEMORY; \
1868 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1870 #include "actionlist.h"
1872 #undef DEFINE_ACTION_MULTI
1873 #undef DEFINE_ACTION_STRING
1874 #undef DEFINE_ACTION_BOOL
1875 #undef DEFINE_ACTION_ALIAS
1876 #undef DEFINE_CGI_PARAM_CUSTOM
1877 #undef DEFINE_CGI_PARAM_RADIO
1878 #undef DEFINE_CGI_PARAM_NO_RADIO
1884 /*********************************************************************
1886 * Function : actions_from_radio
1888 * Description : Converts a map of parameters passed to a CGI function
1889 * into an actionsfile entry.
1892 * 1 : parameters = parameters to the CGI call
1893 * 2 : action = Action to change. Must be valid before
1894 * the call, actions not specified will be
1897 * Returns : JB_ERR_OK on success
1898 * JB_ERR_MEMORY on out-of-memory
1900 *********************************************************************/
1901 static jb_err actions_from_radio(const struct map * parameters,
1902 struct action_spec *action)
1911 #define DEFINE_ACTION_BOOL(name, bit) \
1912 if (NULL != (param = lookup(parameters, name))) \
1914 ch = toupper((int)param[0]); \
1917 action->add |= bit; \
1918 action->mask |= bit; \
1920 else if (ch == 'N') \
1922 action->add &= ~bit; \
1923 action->mask &= ~bit; \
1925 else if (ch == 'X') \
1927 action->add &= ~bit; \
1928 action->mask |= bit; \
1932 #define DEFINE_ACTION_STRING(name, bit, index) \
1933 if (NULL != (param = lookup(parameters, name))) \
1935 ch = toupper((int)param[0]); \
1938 param = lookup(parameters, name "-mode"); \
1939 if ((*param == '\0') || (0 == strcmp(param, "CUSTOM"))) \
1941 param = lookup(parameters, name "-param"); \
1943 if (*param != '\0') \
1945 if (NULL == (param_dup = strdup(param))) \
1947 return JB_ERR_MEMORY; \
1949 freez(action->string[index]); \
1950 action->add |= bit; \
1951 action->mask |= bit; \
1952 action->string[index] = param_dup; \
1955 else if (ch == 'N') \
1957 if (action->add & bit) \
1959 freez(action->string[index]); \
1961 action->add &= ~bit; \
1962 action->mask &= ~bit; \
1964 else if (ch == 'X') \
1966 if (action->add & bit) \
1968 freez(action->string[index]); \
1970 action->add &= ~bit; \
1971 action->mask |= bit; \
1975 #define DEFINE_ACTION_MULTI(name, index) \
1976 if (NULL != (param = lookup(parameters, name))) \
1978 ch = toupper((int)param[0]); \
1983 else if (ch == 'N') \
1985 list_remove_all(action->multi_add[index]); \
1986 list_remove_all(action->multi_remove[index]); \
1987 action->multi_remove_all[index] = 1; \
1989 else if (ch == 'X') \
1991 list_remove_all(action->multi_add[index]); \
1992 list_remove_all(action->multi_remove[index]); \
1993 action->multi_remove_all[index] = 0; \
1997 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val)
1998 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default)
1999 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val)
2001 #define DEFINE_ACTION_ALIAS 0 /* No aliases for URL parsing */
2003 #include "actionlist.h"
2005 #undef DEFINE_ACTION_MULTI
2006 #undef DEFINE_ACTION_STRING
2007 #undef DEFINE_ACTION_BOOL
2008 #undef DEFINE_ACTION_ALIAS
2009 #undef DEFINE_CGI_PARAM_CUSTOM
2010 #undef DEFINE_CGI_PARAM_RADIO
2011 #undef DEFINE_CGI_PARAM_NO_RADIO
2017 /*********************************************************************
2019 * Function : cgi_error_modified
2021 * Description : CGI function that is called when a file is modified
2022 * outside the CGI editor.
2025 * 1 : csp = Current client state (buffers, headers, etc...)
2026 * 2 : rsp = http_response data structure for output
2027 * 3 : filename = The file that was modified.
2029 * CGI Parameters : none
2031 * Returns : JB_ERR_OK on success
2032 * JB_ERR_MEMORY on out-of-memory error.
2034 *********************************************************************/
2035 jb_err cgi_error_modified(struct client_state *csp,
2036 struct http_response *rsp,
2037 const char *filename)
2039 struct map *exports;
2046 if (NULL == (exports = default_exports(csp, NULL)))
2048 return JB_ERR_MEMORY;
2051 err = map(exports, "filename", 1, filename, 1);
2058 return template_fill_for_cgi(csp, "cgi-error-modified", exports, rsp);
2062 /*********************************************************************
2064 * Function : cgi_error_parse
2066 * Description : CGI function that is called when a file cannot
2067 * be parsed by the CGI editor.
2070 * 1 : csp = Current client state (buffers, headers, etc...)
2071 * 2 : rsp = http_response data structure for output
2072 * 3 : file = The file that was modified.
2074 * CGI Parameters : none
2076 * Returns : JB_ERR_OK on success
2077 * JB_ERR_MEMORY on out-of-memory error.
2079 *********************************************************************/
2080 jb_err cgi_error_parse(struct client_state *csp,
2081 struct http_response *rsp,
2082 struct editable_file *file)
2084 struct map *exports;
2086 struct file_line *cur_line;
2092 if (NULL == (exports = default_exports(csp, NULL)))
2094 return JB_ERR_MEMORY;
2097 err = map(exports, "filename", 1, file->identifier, 1);
2098 err = err || map(exports, "parse-error", 1, file->parse_error_text, 1);
2100 cur_line = file->parse_error;
2103 err = err || map(exports, "line-raw", 1, html_encode(cur_line->raw), 0);
2104 err = err || map(exports, "line-data", 1, html_encode(cur_line->unprocessed), 0);
2112 return template_fill_for_cgi(csp, "cgi-error-parse", exports, rsp);
2116 /*********************************************************************
2118 * Function : cgi_error_file
2120 * Description : CGI function that is called when a file cannot be
2121 * opened by the CGI editor.
2124 * 1 : csp = Current client state (buffers, headers, etc...)
2125 * 2 : rsp = http_response data structure for output
2126 * 3 : filename = The file that was modified.
2128 * CGI Parameters : none
2130 * Returns : JB_ERR_OK on success
2131 * JB_ERR_MEMORY on out-of-memory error.
2133 *********************************************************************/
2134 jb_err cgi_error_file(struct client_state *csp,
2135 struct http_response *rsp,
2136 const char *filename)
2138 struct map *exports;
2145 if (NULL == (exports = default_exports(csp, NULL)))
2147 return JB_ERR_MEMORY;
2150 err = map(exports, "filename", 1, filename, 1);
2157 return template_fill_for_cgi(csp, "cgi-error-file", exports, rsp);
2161 /*********************************************************************
2163 * Function : cgi_error_bad_param
2165 * Description : CGI function that is called if the parameters
2166 * (query string) for a CGI were wrong.
2169 * 1 : csp = Current client state (buffers, headers, etc...)
2170 * 2 : rsp = http_response data structure for output
2172 * CGI Parameters : none
2174 * Returns : JB_ERR_OK on success
2175 * JB_ERR_MEMORY on out-of-memory error.
2177 *********************************************************************/
2178 jb_err cgi_error_disabled(struct client_state *csp,
2179 struct http_response *rsp)
2181 struct map *exports;
2186 if (NULL == (exports = default_exports(csp, NULL)))
2188 return JB_ERR_MEMORY;
2191 return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp);
2195 /*********************************************************************
2197 * Function : cgi_edit_actions
2199 * Description : CGI function that allows the user to choose which
2200 * actions file to edit.
2203 * 1 : csp = Current client state (buffers, headers, etc...)
2204 * 2 : rsp = http_response data structure for output
2205 * 3 : parameters = map of cgi parameters
2207 * CGI Parameters : None
2209 * Returns : JB_ERR_OK on success
2210 * JB_ERR_MEMORY on out-of-memory error
2212 *********************************************************************/
2213 jb_err cgi_edit_actions(struct client_state *csp,
2214 struct http_response *rsp,
2215 const struct map *parameters)
2218 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2220 return cgi_error_disabled(csp, rsp);
2223 /* FIXME: Incomplete */
2224 rsp->status = strdup("302 Local Redirect from Junkbuster");
2225 if (rsp->status == NULL)
2227 return JB_ERR_MEMORY;
2229 if (enlist_unique_header(rsp->headers, "Location", "http://ijbswa.sourceforge.net/config/edit-actions-list?filename=edit"))
2233 return JB_ERR_MEMORY;
2240 /*********************************************************************
2242 * Function : cgi_edit_actions_list
2244 * Description : CGI function that edits the actions list.
2245 * FIXME: This function shouldn't FATAL ever.
2246 * FIXME: This function doesn't check the retval of map()
2248 * 1 : csp = Current client state (buffers, headers, etc...)
2249 * 2 : rsp = http_response data structure for output
2250 * 3 : parameters = map of cgi parameters
2252 * CGI Parameters : filename
2254 * Returns : JB_ERR_OK on success
2255 * JB_ERR_MEMORY on out-of-memory
2256 * JB_ERR_FILE if the file cannot be opened or
2258 * JB_ERR_CGI_PARAMS if "filename" was not specified
2261 *********************************************************************/
2262 jb_err cgi_edit_actions_list(struct client_state *csp,
2263 struct http_response *rsp,
2264 const struct map *parameters)
2266 char * section_template;
2267 char * url_template;
2272 struct map * exports;
2273 struct map * section_exports;
2274 struct map * url_exports;
2275 struct editable_file * file;
2276 struct file_line * cur_line;
2277 int line_number = 0;
2281 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2283 return cgi_error_disabled(csp, rsp);
2286 err = edit_read_actions_file(csp, rsp, parameters, 0, &file);
2289 /* No filename specified, can't read file, or out of memory. */
2290 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2293 if (NULL == (exports = default_exports(csp, NULL)))
2295 edit_free_file(file);
2296 return JB_ERR_MEMORY;
2299 err = map(exports, "filename", 1, file->identifier, 1);
2300 err = err || map(exports, "ver", 1, file->version_str, 1);
2303 edit_free_file(file);
2308 /* Should do all global exports above this point */
2310 err = template_load(csp, §ion_template, "edit-actions-list-section");
2313 edit_free_file(file);
2315 if (err == JB_ERR_FILE)
2317 return cgi_error_no_template(csp, rsp, "edit-actions-list-section");
2322 err = template_load(csp, &url_template, "edit-actions-list-url");
2325 free(section_template);
2326 edit_free_file(file);
2328 if (err == JB_ERR_FILE)
2330 return cgi_error_no_template(csp, rsp, "edit-actions-list-url");
2335 err = template_fill(§ion_template, exports);
2339 edit_free_file(file);
2345 err = template_fill(&url_template, exports);
2348 free(section_template);
2349 edit_free_file(file);
2354 /* Find start of actions in file */
2355 cur_line = file->lines;
2357 while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION))
2359 cur_line = cur_line->next;
2363 if (NULL == (sections = strdup("")))
2365 free(section_template);
2367 edit_free_file(file);
2369 return JB_ERR_MEMORY;
2372 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_ACTION))
2374 if (NULL == (section_exports = new_map()))
2377 free(section_template);
2379 edit_free_file(file);
2381 return JB_ERR_MEMORY;
2384 snprintf(buf, 50, "%d", line_number);
2385 err = map(section_exports, "sectionid", 1, buf, 1);
2387 err = err || map(section_exports, "actions", 1,
2388 actions_to_html(cur_line->data.action), 0);
2390 if ((cur_line->next != NULL) && (cur_line->next->type == FILE_LINE_URL))
2392 /* This section contains at least one URL, don't allow delete */
2393 err = err || map_block_killer(section_exports, "empty-section");
2399 free(section_template);
2401 edit_free_file(file);
2403 free_map(section_exports);
2407 /* Should do all section-specific exports above this point */
2409 if (NULL == (urls = strdup("")))
2412 free(section_template);
2414 edit_free_file(file);
2416 free_map(section_exports);
2417 return JB_ERR_MEMORY;
2422 cur_line = cur_line->next;
2425 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL))
2427 if (NULL == (url_exports = new_map()))
2431 free(section_template);
2433 edit_free_file(file);
2435 free_map(section_exports);
2436 return JB_ERR_MEMORY;
2439 snprintf(buf, 50, "%d", line_number);
2440 err = map(url_exports, "urlid", 1, buf, 1);
2442 snprintf(buf, 50, "%d", url_1_2);
2443 err = err || map(url_exports, "url-1-2", 1, buf, 1);
2445 err = err || map(url_exports, "url", 1,
2446 html_encode(cur_line->unprocessed), 0);
2452 free(section_template);
2454 edit_free_file(file);
2456 free_map(section_exports);
2457 free_map(url_exports);
2461 if (NULL == (s = strdup(url_template)))
2465 free(section_template);
2467 edit_free_file(file);
2469 free_map(section_exports);
2470 free_map(url_exports);
2471 return JB_ERR_MEMORY;
2474 err = template_fill(&s, section_exports);
2475 err = err || template_fill(&s, url_exports);
2476 err = err || string_append(&urls, s);
2477 free_map(url_exports);
2484 free(section_template);
2486 edit_free_file(file);
2488 free_map(section_exports);
2492 url_1_2 = 3 - url_1_2;
2494 cur_line = cur_line->next;
2498 err = map(section_exports, "urls", 1, urls, 0);
2503 free(section_template);
2505 edit_free_file(file);
2507 free_map(section_exports);
2511 /* Could also do section-specific exports here, but it wouldn't be as fast */
2513 if (NULL == (s = strdup(section_template)))
2516 free(section_template);
2518 edit_free_file(file);
2520 free_map(section_exports);
2521 return JB_ERR_MEMORY;
2524 err = template_fill(&s, section_exports);
2525 err = err || string_append(§ions, s);
2527 free_map(section_exports);
2532 free(section_template);
2534 edit_free_file(file);
2540 edit_free_file(file);
2541 free(section_template);
2544 err = map(exports, "sections", 1, sections, 0);
2551 /* Could also do global exports here, but it wouldn't be as fast */
2553 return template_fill_for_cgi(csp, "edit-actions-list", exports, rsp);
2557 /*********************************************************************
2559 * Function : cgi_edit_actions
2561 * Description : CGI function that edits the Actions list.
2564 * 1 : csp = Current client state (buffers, headers, etc...)
2565 * 2 : rsp = http_response data structure for output
2566 * 3 : parameters = map of cgi parameters
2568 * CGI Parameters : None
2570 * Returns : JB_ERR_OK on success
2571 * JB_ERR_MEMORY on out-of-memory
2572 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2573 * specified or not valid.
2575 *********************************************************************/
2576 jb_err cgi_edit_actions_for_url(struct client_state *csp,
2577 struct http_response *rsp,
2578 const struct map *parameters)
2580 struct map * exports;
2582 struct editable_file * file;
2583 struct file_line * cur_line;
2584 unsigned line_number;
2587 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2589 return cgi_error_disabled(csp, rsp);
2592 err = get_number_param(csp, parameters, "section", §ionid);
2598 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2601 /* No filename specified, can't read file, modified, or out of memory. */
2602 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2605 cur_line = file->lines;
2607 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2609 cur_line = cur_line->next;
2612 if ( (cur_line == NULL)
2613 || (line_number != sectionid)
2615 || (cur_line->type != FILE_LINE_ACTION))
2617 /* Invalid "sectionid" parameter */
2618 edit_free_file(file);
2619 return JB_ERR_CGI_PARAMS;
2622 if (NULL == (exports = default_exports(csp, NULL)))
2624 edit_free_file(file);
2625 return JB_ERR_MEMORY;
2628 err = map(exports, "filename", 1, file->identifier, 1);
2629 err = err || map(exports, "ver", 1, file->version_str, 1);
2630 err = err || map(exports, "section", 1, lookup(parameters, "section"), 1);
2632 err = err || actions_to_radio(exports, cur_line->data.action);
2634 edit_free_file(file);
2642 return template_fill_for_cgi(csp, "edit-actions-for-url", exports, rsp);
2646 /*********************************************************************
2648 * Function : cgi_edit_actions_submit
2650 * Description : CGI function that actually edits the Actions list.
2653 * 1 : csp = Current client state (buffers, headers, etc...)
2654 * 2 : rsp = http_response data structure for output
2655 * 3 : parameters = map of cgi parameters
2657 * CGI Parameters : None
2659 * Returns : JB_ERR_OK on success
2660 * JB_ERR_MEMORY on out-of-memory
2661 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2662 * specified or not valid.
2664 *********************************************************************/
2665 jb_err cgi_edit_actions_submit(struct client_state *csp,
2666 struct http_response *rsp,
2667 const struct map *parameters)
2673 struct editable_file * file;
2674 struct file_line * cur_line;
2679 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2681 return cgi_error_disabled(csp, rsp);
2684 err = get_number_param(csp, parameters, "section", §ionid);
2690 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2693 /* No filename specified, can't read file, modified, or out of memory. */
2694 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2697 cur_line = file->lines;
2699 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2701 cur_line = cur_line->next;
2704 if ( (cur_line == NULL)
2705 || (line_number != sectionid)
2707 || (cur_line->type != FILE_LINE_ACTION))
2709 /* Invalid "sectionid" parameter */
2710 edit_free_file(file);
2711 return JB_ERR_CGI_PARAMS;
2714 err = actions_from_radio(parameters, cur_line->data.action);
2718 edit_free_file(file);
2722 if (NULL == (actiontext = actions_to_text(cur_line->data.action)))
2725 edit_free_file(file);
2726 return JB_ERR_MEMORY;
2729 len = strlen(actiontext);
2733 * Empty action - must special-case this.
2734 * Simply setting len to 1 is sufficient...
2739 if (NULL == (newtext = malloc(len + 2)))
2743 edit_free_file(file);
2744 return JB_ERR_MEMORY;
2746 strcpy(newtext, actiontext);
2750 newtext[len + 1] = '\0';
2752 freez(cur_line->raw);
2753 freez(cur_line->unprocessed);
2754 cur_line->unprocessed = newtext;
2756 err = edit_write_file(file);
2759 /* Error writing file */
2760 edit_free_file(file);
2764 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
2765 string_append(&target, file->identifier);
2767 edit_free_file(file);
2772 return JB_ERR_MEMORY;
2775 rsp->status = strdup("302 Local Redirect from Junkbuster");
2776 if (rsp->status == NULL)
2779 return JB_ERR_MEMORY;
2781 err = enlist_unique_header(rsp->headers, "Location", target);
2788 /*********************************************************************
2790 * Function : cgi_edit_actions_url
2792 * Description : CGI function that actually edits a URL pattern in
2796 * 1 : csp = Current client state (buffers, headers, etc...)
2797 * 2 : rsp = http_response data structure for output
2798 * 3 : parameters = map of cgi parameters
2801 * filename : Identifies the file to edit
2802 * ver : File's last-modified time
2803 * section : Line number of section to edit
2804 * pattern : Line number of pattern to edit
2805 * newval : New value for pattern
2807 * Returns : JB_ERR_OK on success
2808 * JB_ERR_MEMORY on out-of-memory
2809 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2810 * specified or not valid.
2812 *********************************************************************/
2813 jb_err cgi_edit_actions_url(struct client_state *csp,
2814 struct http_response *rsp,
2815 const struct map *parameters)
2819 const char * newval;
2821 struct editable_file * file;
2822 struct file_line * cur_line;
2823 unsigned line_number;
2827 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2829 return cgi_error_disabled(csp, rsp);
2832 err = get_number_param(csp, parameters, "section", §ionid);
2833 err = err || get_number_param(csp, parameters, "pattern", &patternid);
2839 newval = lookup(parameters, "newval");
2841 if ((*newval == '\0') || (sectionid < 1U) || (patternid < 1U))
2843 return JB_ERR_CGI_PARAMS;
2846 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2849 /* No filename specified, can't read file, modified, or out of memory. */
2850 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2854 cur_line = file->lines;
2856 while ((cur_line != NULL) && (line_number < sectionid))
2858 cur_line = cur_line->next;
2862 if ( (cur_line == NULL)
2863 || (cur_line->type != FILE_LINE_ACTION))
2865 /* Invalid "sectionid" parameter */
2866 edit_free_file(file);
2867 return JB_ERR_CGI_PARAMS;
2870 while (line_number < patternid)
2872 cur_line = cur_line->next;
2875 if ( (cur_line == NULL)
2876 || ( (cur_line->type != FILE_LINE_URL)
2877 && (cur_line->type != FILE_LINE_BLANK) ) )
2879 /* Invalid "patternid" parameter */
2880 edit_free_file(file);
2881 return JB_ERR_CGI_PARAMS;
2885 if (cur_line->type != FILE_LINE_URL)
2887 /* Invalid "patternid" parameter */
2888 edit_free_file(file);
2889 return JB_ERR_CGI_PARAMS;
2892 /* At this point, the line to edit is in cur_line */
2894 new_pattern = strdup(newval);
2895 if (NULL == new_pattern)
2897 edit_free_file(file);
2898 return JB_ERR_MEMORY;
2901 freez(cur_line->raw);
2902 freez(cur_line->unprocessed);
2903 cur_line->unprocessed = new_pattern;
2905 err = edit_write_file(file);
2908 /* Error writing file */
2909 edit_free_file(file);
2913 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
2914 string_append(&target, file->identifier);
2916 edit_free_file(file);
2921 return JB_ERR_MEMORY;
2924 rsp->status = strdup("302 Local Redirect from Junkbuster");
2925 if (rsp->status == NULL)
2928 return JB_ERR_MEMORY;
2930 err = enlist_unique_header(rsp->headers, "Location", target);
2937 /*********************************************************************
2939 * Function : cgi_edit_actions_add_url
2941 * Description : CGI function that actually adds a URL pattern to
2945 * 1 : csp = Current client state (buffers, headers, etc...)
2946 * 2 : rsp = http_response data structure for output
2947 * 3 : parameters = map of cgi parameters
2950 * filename : Identifies the file to edit
2951 * ver : File's last-modified time
2952 * section : Line number of section to edit
2953 * newval : New pattern
2955 * Returns : JB_ERR_OK on success
2956 * JB_ERR_MEMORY on out-of-memory
2957 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2958 * specified or not valid.
2960 *********************************************************************/
2961 jb_err cgi_edit_actions_add_url(struct client_state *csp,
2962 struct http_response *rsp,
2963 const struct map *parameters)
2967 const char * newval;
2969 struct file_line * new_line;
2970 struct editable_file * file;
2971 struct file_line * cur_line;
2972 unsigned line_number;
2976 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2978 return cgi_error_disabled(csp, rsp);
2981 err = get_number_param(csp, parameters, "section", §ionid);
2987 newval = lookup(parameters, "newval");
2989 if ((*newval == '\0') || (sectionid < 1U) || (patternid < 1U))
2991 return JB_ERR_CGI_PARAMS;
2994 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2997 /* No filename specified, can't read file, modified, or out of memory. */
2998 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3002 cur_line = file->lines;
3004 while ((cur_line != NULL) && (line_number < sectionid))
3006 cur_line = cur_line->next;
3010 if ( (cur_line == NULL)
3011 || (cur_line->type != FILE_LINE_ACTION))
3013 /* Invalid "sectionid" parameter */
3014 edit_free_file(file);
3015 return JB_ERR_CGI_PARAMS;
3018 /* At this point, the section header is in cur_line - add after this. */
3020 new_pattern = strdup(newval);
3021 if (NULL == new_pattern)
3023 edit_free_file(file);
3024 return JB_ERR_MEMORY;
3027 /* Allocate the new line */
3028 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3029 if (new_line == NULL)
3032 edit_free_file(file);
3033 return JB_ERR_MEMORY;
3036 /* Fill in the data members of the new line */
3037 new_line->raw = NULL;
3038 new_line->prefix = NULL;
3039 new_line->unprocessed = new_pattern;
3040 new_line->type = FILE_LINE_URL;
3042 /* Link new_line into the list, after cur_line */
3043 new_line->next = cur_line->next;
3044 cur_line->next = new_line;
3046 /* Done making changes, now commit */
3048 err = edit_write_file(file);
3051 /* Error writing file */
3052 edit_free_file(file);
3056 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3057 string_append(&target, file->identifier);
3059 edit_free_file(file);
3064 return JB_ERR_MEMORY;
3067 rsp->status = strdup("302 Local Redirect from Junkbuster");
3068 if (rsp->status == NULL)
3071 return JB_ERR_MEMORY;
3073 err = enlist_unique_header(rsp->headers, "Location", target);
3080 /*********************************************************************
3082 * Function : cgi_edit_actions_remove_url
3084 * Description : CGI function that actually removes a URL pattern from
3088 * 1 : csp = Current client state (buffers, headers, etc...)
3089 * 2 : rsp = http_response data structure for output
3090 * 3 : parameters = map of cgi parameters
3093 * filename : Identifies the file to edit
3094 * ver : File's last-modified time
3095 * section : Line number of section to edit
3096 * pattern : Line number of pattern to edit
3098 * Returns : JB_ERR_OK on success
3099 * JB_ERR_MEMORY on out-of-memory
3100 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3101 * specified or not valid.
3103 *********************************************************************/
3104 jb_err cgi_edit_actions_remove_url(struct client_state *csp,
3105 struct http_response *rsp,
3106 const struct map *parameters)
3110 struct editable_file * file;
3111 struct file_line * cur_line;
3112 struct file_line * prev_line;
3113 unsigned line_number;
3117 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3119 return cgi_error_disabled(csp, rsp);
3122 err = get_number_param(csp, parameters, "section", §ionid);
3123 err = err || get_number_param(csp, parameters, "pattern", &patternid);
3130 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3133 /* No filename specified, can't read file, modified, or out of memory. */
3134 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3138 cur_line = file->lines;
3140 while ((cur_line != NULL) && (line_number < sectionid))
3142 cur_line = cur_line->next;
3146 if ( (cur_line == NULL)
3147 || (cur_line->type != FILE_LINE_ACTION))
3149 /* Invalid "sectionid" parameter */
3150 edit_free_file(file);
3151 return JB_ERR_CGI_PARAMS;
3155 while (line_number < patternid)
3157 prev_line = cur_line;
3158 cur_line = cur_line->next;
3161 if ( (cur_line == NULL)
3162 || ( (cur_line->type != FILE_LINE_URL)
3163 && (cur_line->type != FILE_LINE_BLANK) ) )
3165 /* Invalid "patternid" parameter */
3166 edit_free_file(file);
3167 return JB_ERR_CGI_PARAMS;
3171 if (cur_line->type != FILE_LINE_URL)
3173 /* Invalid "patternid" parameter */
3174 edit_free_file(file);
3175 return JB_ERR_CGI_PARAMS;
3180 /* At this point, the line to remove is in cur_line, and the previous
3181 * one is in prev_line
3184 /* Unlink cur_line */
3185 prev_line->next = cur_line->next;
3186 cur_line->next = NULL;
3189 edit_free_file_lines(cur_line);
3191 err = edit_write_file(file);
3194 /* Error writing file */
3195 edit_free_file(file);
3199 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3200 string_append(&target, file->identifier);
3202 edit_free_file(file);
3207 return JB_ERR_MEMORY;
3210 rsp->status = strdup("302 Local Redirect from Junkbuster");
3211 if (rsp->status == NULL)
3214 return JB_ERR_MEMORY;
3216 err = enlist_unique_header(rsp->headers, "Location", target);
3223 /*********************************************************************
3225 * Function : cgi_edit_actions_section_remove
3227 * Description : CGI function that actually removes a whole section from
3228 * the actions file. The section must be empty first
3229 * (else JB_ERR_CGI_PARAMS).
3232 * 1 : csp = Current client state (buffers, headers, etc...)
3233 * 2 : rsp = http_response data structure for output
3234 * 3 : parameters = map of cgi parameters
3237 * filename : Identifies the file to edit
3238 * ver : File's last-modified time
3239 * section : Line number of section to edit
3240 * pattern : Line number of pattern to edit
3242 * Returns : JB_ERR_OK on success
3243 * JB_ERR_MEMORY on out-of-memory
3244 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3245 * specified or not valid.
3247 *********************************************************************/
3248 jb_err cgi_edit_actions_section_remove(struct client_state *csp,
3249 struct http_response *rsp,
3250 const struct map *parameters)
3253 struct editable_file * file;
3254 struct file_line * cur_line;
3255 struct file_line * prev_line;
3256 unsigned line_number;
3260 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3262 return cgi_error_disabled(csp, rsp);
3265 err = get_number_param(csp, parameters, "section", §ionid);
3271 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3274 /* No filename specified, can't read file, modified, or out of memory. */
3275 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3279 cur_line = file->lines;
3282 while ((cur_line != NULL) && (line_number < sectionid))
3284 prev_line = cur_line;
3285 cur_line = cur_line->next;
3289 if ( (cur_line == NULL)
3290 || (cur_line->type != FILE_LINE_ACTION) )
3292 /* Invalid "sectionid" parameter */
3293 edit_free_file(file);
3294 return JB_ERR_CGI_PARAMS;
3297 if ( (cur_line->next != NULL)
3298 && (cur_line->next->type == FILE_LINE_URL) )
3300 /* Section not empty. */
3301 edit_free_file(file);
3302 return JB_ERR_CGI_PARAMS;
3305 /* At this point, the line to remove is in cur_line, and the previous
3306 * one is in prev_line
3309 /* Unlink cur_line */
3310 if (prev_line == NULL)
3312 /* Removing the first line from the file */
3313 file->lines = cur_line->next;
3317 prev_line->next = cur_line->next;
3319 cur_line->next = NULL;
3322 edit_free_file_lines(cur_line);
3324 err = edit_write_file(file);
3327 /* Error writing file */
3328 edit_free_file(file);
3332 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3333 string_append(&target, file->identifier);
3335 edit_free_file(file);
3340 return JB_ERR_MEMORY;
3343 rsp->status = strdup("302 Local Redirect from Junkbuster");
3344 if (rsp->status == NULL)
3347 return JB_ERR_MEMORY;
3349 err = enlist_unique_header(rsp->headers, "Location", target);
3356 /*********************************************************************
3358 * Function : cgi_edit_actions_section_add
3360 * Description : CGI function that adds a new empty section to
3364 * 1 : csp = Current client state (buffers, headers, etc...)
3365 * 2 : rsp = http_response data structure for output
3366 * 3 : parameters = map of cgi parameters
3369 * filename : Identifies the file to edit
3370 * ver : File's last-modified time
3371 * section : Line number of section to add after, 0 for start
3374 * Returns : JB_ERR_OK on success
3375 * JB_ERR_MEMORY on out-of-memory
3376 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3377 * specified or not valid.
3379 *********************************************************************/
3380 jb_err cgi_edit_actions_section_add(struct client_state *csp,
3381 struct http_response *rsp,
3382 const struct map *parameters)
3385 struct file_line * new_line;
3387 struct editable_file * file;
3388 struct file_line * cur_line;
3389 unsigned line_number;
3393 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3395 return cgi_error_disabled(csp, rsp);
3398 err = get_number_param(csp, parameters, "section", §ionid);
3404 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3407 /* No filename specified, can't read file, modified, or out of memory. */
3408 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3412 cur_line = file->lines;
3416 /* Add to start of file */
3417 if (cur_line != NULL)
3419 /* There's something in the file, find the line before the first
3422 while ( (cur_line->next != NULL)
3423 && (cur_line->next->type != FILE_LINE_ACTION) )
3425 cur_line = cur_line->next;
3432 /* Add after stated section. */
3433 while ((cur_line != NULL) && (line_number < sectionid))
3435 cur_line = cur_line->next;
3439 if ( (cur_line == NULL)
3440 || (cur_line->type != FILE_LINE_ACTION))
3442 /* Invalid "sectionid" parameter */
3443 edit_free_file(file);
3444 return JB_ERR_CGI_PARAMS;
3447 /* Skip through the section to find the last line in it. */
3448 while ( (cur_line->next != NULL)
3449 && (cur_line->next->type != FILE_LINE_ACTION) )
3451 cur_line = cur_line->next;
3456 /* At this point, the last line in the previous section is in cur_line
3457 * - add after this. (Or if we need to add as the first line, cur_line
3461 new_text = strdup("{}");
3462 if (NULL == new_text)
3464 edit_free_file(file);
3465 return JB_ERR_MEMORY;
3468 /* Allocate the new line */
3469 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3470 if (new_line == NULL)
3473 edit_free_file(file);
3474 return JB_ERR_MEMORY;
3477 /* Fill in the data members of the new line */
3478 new_line->raw = NULL;
3479 new_line->prefix = NULL;
3480 new_line->unprocessed = new_text;
3481 new_line->type = FILE_LINE_ACTION;
3483 if (cur_line != NULL)
3485 /* Link new_line into the list, after cur_line */
3486 new_line->next = cur_line->next;
3487 cur_line->next = new_line;
3491 /* Link new_line into the list, as first line */
3492 new_line->next = file->lines;
3493 file->lines = new_line;
3496 /* Done making changes, now commit */
3498 err = edit_write_file(file);
3501 /* Error writing file */
3502 edit_free_file(file);
3506 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3507 string_append(&target, file->identifier);
3509 edit_free_file(file);
3514 return JB_ERR_MEMORY;
3517 rsp->status = strdup("302 Local Redirect from Junkbuster");
3518 if (rsp->status == NULL)
3521 return JB_ERR_MEMORY;
3523 err = enlist_unique_header(rsp->headers, "Location", target);
3530 /*********************************************************************
3532 * Function : cgi_toggle
3534 * Description : CGI function that adds a new empty section to
3538 * 1 : csp = Current client state (buffers, headers, etc...)
3539 * 2 : rsp = http_response data structure for output
3540 * 3 : parameters = map of cgi parameters
3543 * set : If present, how to change toggle setting:
3544 * "enable", "disable", "toggle", or none (default).
3545 * mini : If present, use mini reply template.
3547 * Returns : JB_ERR_OK on success
3548 * JB_ERR_MEMORY on out-of-memory
3550 *********************************************************************/
3551 jb_err cgi_toggle(struct client_state *csp,
3552 struct http_response *rsp,
3553 const struct map *parameters)
3555 struct map *exports;
3557 const char *template_name;
3564 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE))
3566 return cgi_error_disabled(csp, rsp);
3569 if (NULL == (exports = default_exports(csp, "toggle")))
3571 return JB_ERR_MEMORY;
3574 mode = *(lookup(parameters, "set"));
3581 else if (mode == 'd')
3586 else if (mode == 't')
3589 g_bToggleIJB = !g_bToggleIJB;
3592 err = map_conditional(exports, "enabled", g_bToggleIJB);
3599 template_name = (*(lookup(parameters, "mini"))
3603 return template_fill_for_cgi(csp, template_name, exports, rsp);
3605 #endif /* def FEATURE_CGI_EDIT_ACTIONS */