1 const char cgiedit_rcs[] = "$Id: cgiedit.c,v 1.5 2001/10/25 03:40:48 david__schmidt Exp $";
2 /*********************************************************************
4 * File : $Source: /cvsroot/ijbswa/current/cgiedit.c,v $
6 * Purpose : CGI-based actionsfile editor.
8 * Functions declared include:
11 * Copyright : Written by and Copyright (C) 2001 the SourceForge
12 * IJBSWA team. http://ijbswa.sourceforge.net
14 * Based on the Internet Junkbuster originally written
15 * by and Copyright (C) 1997 Anonymous Coders and
16 * Junkbusters Corporation. http://www.junkbusters.com
18 * This program is free software; you can redistribute it
19 * and/or modify it under the terms of the GNU General
20 * Public License as published by the Free Software
21 * Foundation; either version 2 of the License, or (at
22 * your option) any later version.
24 * This program is distributed in the hope that it will
25 * be useful, but WITHOUT ANY WARRANTY; without even the
26 * implied warranty of MERCHANTABILITY or FITNESS FOR A
27 * PARTICULAR PURPOSE. See the GNU General Public
28 * License for more details.
30 * The GNU General Public License should be included with
31 * this file. If not, you can view it at
32 * http://www.gnu.org/copyleft/gpl.html
33 * or write to the Free Software Foundation, Inc., 59
34 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
38 * Revision 1.5 2001/10/25 03:40:48 david__schmidt
39 * Change in porting tactics: OS/2's EMX porting layer doesn't allow multiple
40 * threads to call select() simultaneously. So, it's time to do a real, live,
41 * native OS/2 port. See defines for __EMX__ (the porting layer) vs. __OS2__
42 * (native). Both versions will work, but using __OS2__ offers multi-threading.
44 * Revision 1.4 2001/10/23 21:48:19 jongfoster
45 * Cleaning up error handling in CGI functions - they now send back
46 * a HTML error page and should never cause a FATAL error. (Fixes one
47 * potential source of "denial of service" attacks).
49 * CGI actions file editor that works and is actually useful.
51 * Ability to toggle JunkBuster remotely using a CGI call.
53 * You can turn off both the above features in the main configuration
54 * file, e.g. if you are running a multi-user proxy.
56 * Revision 1.3 2001/10/14 22:12:49 jongfoster
57 * New version of CGI-based actionsfile editor.
58 * Major changes, including:
59 * - Completely new file parser and file output routines
60 * - edit-actions CGI renamed edit-actions-for-url
61 * - All CGIs now need a filename parameter, except for...
62 * - New CGI edit-actions which doesn't need a filename,
63 * to allow you to start the editor up.
64 * - edit-actions-submit now works, and now automatically
65 * redirects you back to the main edit-actions-list handler.
67 * Revision 1.2 2001/09/16 17:05:14 jongfoster
68 * Removing unused #include showarg.h
70 * Revision 1.1 2001/09/16 15:47:37 jongfoster
71 * First version of CGI-based edit interface. This is very much a
72 * work-in-progress, and you can't actually use it to edit anything
73 * yet. You must #define FEATURE_CGI_EDIT_ACTIONS for these changes
77 **********************************************************************/
83 * FIXME: Following includes copied from cgi.c - which are actually needed?
88 #include <sys/types.h>
97 #define snprintf _snprintf
98 #endif /* def _WIN32 */
103 #include "cgisimple.h"
107 #include "miscutil.h"
110 /* loadcfg.h is for g_bToggleIJB only */
112 const char cgiedit_h_rcs[] = CGIEDIT_H_VERSION;
115 #ifdef FEATURE_CGI_EDIT_ACTIONS
119 struct file_line * next;
127 struct action_spec action[1];
136 /* Add more data types here... e.g.
139 struct url_spec url[1];
143 struct action_spec action[1];
152 #define FILE_LINE_UNPROCESSED 1
153 #define FILE_LINE_BLANK 2
154 #define FILE_LINE_ALIAS_HEADER 3
155 #define FILE_LINE_ALIAS_ENTRY 4
156 #define FILE_LINE_ACTION 5
157 #define FILE_LINE_URL 6
158 #define FILE_LINE_SETTINGS_HEADER 7
159 #define FILE_LINE_SETTINGS_ENTRY 8
160 #define FILE_LINE_DESCRIPTION_HEADER 9
161 #define FILE_LINE_DESCRIPTION_ENTRY 10
166 struct file_line * lines;
167 const char * filename; /* Full pathname - e.g. "/etc/junkbuster/wibble.action" */
168 const char * identifier; /* Filename stub - e.g. "wibble". Use for CGI param. */
169 const char * version_str; /* Last modification time, as a string. For CGI param */
170 unsigned version; /* Last modification time - prevents chaos with
171 * the browser's "back" button. Note that this is a
172 * time_t cast to an unsigned. When comparing, always
173 * cast the time_t to an unsigned, and *NOT* vice-versa.
174 * This may lose the top few bits, but they're not
175 * significant anyway.
177 struct file_line * parse_error; /* On parse error, this is the offending line. */
178 const char * parse_error_text; /* On parse error, this is the problem.
179 * (Statically allocated) */
182 /* FIXME: Following non-static functions should be prototyped in .h or made static */
184 /* Functions to read and write arbitrary config files */
185 jb_err edit_read_file(struct client_state *csp,
186 const struct map *parameters,
189 struct editable_file **pfile);
190 jb_err edit_write_file(struct editable_file * file);
191 void edit_free_file(struct editable_file * file);
193 /* Functions to read and write actions files */
194 jb_err edit_parse_actions_file(struct editable_file * file);
195 jb_err edit_read_actions_file(struct client_state *csp,
196 struct http_response *rsp,
197 const struct map *parameters,
199 struct editable_file **pfile);
202 jb_err cgi_error_modified(struct client_state *csp,
203 struct http_response *rsp,
204 const char *filename);
205 jb_err cgi_error_parse(struct client_state *csp,
206 struct http_response *rsp,
207 struct editable_file *file);
208 jb_err cgi_error_file(struct client_state *csp,
209 struct http_response *rsp,
210 const char *filename);
212 /* Internal arbitrary config file support functions */
213 static jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile);
214 static void edit_free_file_lines(struct file_line * first_line);
215 static jb_err simple_read_line(char **dest, FILE *fp);
216 static jb_err edit_read_line(FILE *fp, char **raw_out, char **prefix_out, char **data_out);
218 /* Internal actions file support functions */
219 static int match_actions_file_header_line(const char * line, const char * name);
220 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue);
222 /* Internal parameter parsing functions */
223 static jb_err get_file_name_param(struct client_state *csp,
224 const struct map *parameters,
225 const char *param_name,
228 const char **pparam);
229 static jb_err get_number_param(struct client_state *csp,
230 const struct map *parameters,
234 /* Internal actionsfile <==> HTML conversion functions */
235 static jb_err map_radio(struct map * exports,
236 const char * optionname,
239 static jb_err actions_to_radio(struct map * exports,
240 const struct action_spec *action);
241 static jb_err actions_from_radio(const struct map * parameters,
242 struct action_spec *action);
245 /*********************************************************************
247 * Function : simple_read_line
249 * Description : Read a single line from a file and return it.
250 * This is basically a version of fgets() that malloc()s
251 * it's own line buffer. Note that the buffer will
252 * always be a multiple of BUFFER_SIZE bytes long.
253 * Therefore if you are going to keep the string for
254 * an extended period of time, you should probably
255 * strdup() it and free() the original, to save memory.
259 * 1 : dest = destination for newly malloc'd pointer to
260 * line data. Will be set to NULL on error.
261 * 2 : fp = File to read from
263 * Returns : JB_ERR_OK on success
264 * JB_ERR_MEMORY on out-of-memory
265 * JB_ERR_FILE on EOF.
267 *********************************************************************/
268 static jb_err simple_read_line(char **dest, FILE *fp)
279 if (NULL == (buf = malloc(BUFFER_SIZE)))
281 return JB_ERR_MEMORY;
290 if ((!fgets(newbuf, BUFFER_SIZE, fp)) || (*newbuf == '\0'))
292 /* (*newbuf == '\0') should never happen unless fgets fails */
305 if ((buf[len - 1] == '\n') || (buf[len - 1] == '\r'))
311 if (NULL == (newbuf = realloc(buf, len + BUFFER_SIZE)))
314 return JB_ERR_MEMORY;
321 /*********************************************************************
323 * Function : edit_read_line
325 * Description : Read a single non-empty line from a file and return
326 * it. Trims comments, leading and trailing whitespace
327 * and respects escaping of newline and comment char.
328 * Provides the line in 2 alternative forms: raw and
330 * - raw is the raw data read from the file. If the
331 * line is not modified, then this should be written
333 * - prefix is any comments and blank lines that were
334 * read from the file. If the line is modified, then
335 * this should be written out to the file followed
336 * by the modified data. (If this string is non-empty
337 * then it will have a newline at the end).
338 * - data is the actual data that will be parsed
339 * further by appropriate routines.
340 * On EOF, the 3 strings will all be set to NULL and
341 * 0 will be returned.
344 * 1 : fp = File to read from
345 * 2 : raw_out = destination for newly malloc'd pointer to
346 * raw line data. May be NULL if you don't want it.
347 * 3 : prefix_out = destination for newly malloc'd pointer to
348 * comments. May be NULL if you don't want it.
349 * 4 : data_out = destination for newly malloc'd pointer to
350 * line data with comments and leading/trailing spaces
351 * removed, and line continuation performed. May be
352 * NULL if you don't want it.
354 * Returns : JB_ERR_OK on success
355 * JB_ERR_MEMORY on out-of-memory
356 * JB_ERR_FILE on EOF.
358 *********************************************************************/
359 static jb_err edit_read_line(FILE *fp, char **raw_out, char **prefix_out, char **data_out)
361 char *p; /* Temporary pointer */
362 char *linebuf; /* Line read from file */
363 char *linestart; /* Start of linebuf, usually first non-whitespace char */
364 char newline[3]; /* Used to store the newline - "\n", "\r", or "\r\n" */
365 int contflag = 0; /* Nonzero for line continuation - i.e. line ends '\' */
366 char *raw; /* String to be stored in raw_out */
367 char *prefix; /* String to be stored in prefix_out */
368 char *data; /* String to be stored in data_out */
369 jb_err rval = JB_ERR_OK;
373 /* Set output parameters to NULL */
387 /* Set string variables to new, empty strings. */
393 if ((raw == NULL) || (prefix == NULL) || (data == NULL))
398 return JB_ERR_MEMORY;
405 /* Main loop. Loop while we need more data & it's not EOF. */
407 while ( (contflag || (*data == '\0'))
408 && (JB_ERR_OK == (rval = simple_read_line(&linebuf, fp))))
410 if (string_append(&raw,linebuf))
415 return JB_ERR_MEMORY;
418 /* Trim off newline */
419 p = linebuf + strlen(linebuf);
420 if ((p != linebuf) && ((p[-1] == '\r') || (p[-1] == '\n')))
423 if ((p != linebuf) && ((p[-1] == '\r') || (p[-1] == '\n')))
431 /* Line continuation? Trim escape and set flag. */
432 contflag = ((p != linebuf) && (*--p == '\\'));
438 /* Trim leading spaces if we're at the start of the line */
442 /* Trim leading spaces */
443 while (*linestart && isspace((int)(unsigned char)*linestart))
449 /* Handle comment characters. */
451 while ((p = strchr(p, '#')) != NULL)
453 /* Found a comment char.. */
454 if ((p != linebuf) && (*(p-1) == '\\'))
456 /* ..and it's escaped, left-shift the line over the escape. */
458 while ((*q = *(q + 1)) != '\0')
462 /* Now scan from just after the "#". */
466 /* Real comment. Save it... */
469 /* Special case: Line only contains a comment, so all the
470 * previous whitespace is considered part of the comment.
471 * Undo the whitespace skipping, if any.
476 string_append(&prefix,p);
477 if (string_append(&prefix,newline))
482 return JB_ERR_MEMORY;
486 /* ... and chop off the rest of the line */
489 } /* END while (there's a # character) */
491 /* Write to the buffer */
494 if (string_append(&data, linestart))
499 return JB_ERR_MEMORY;
504 } /* END while(we need more data) */
506 /* Handle simple_read_line() errors - ignore EOF */
507 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
518 /* Got at least some data */
520 /* Remove trailing whitespace */
533 *prefix_out = prefix;
551 /* EOF and no data there. */
562 /*********************************************************************
564 * Function : edit_write_file
566 * Description : Write a complete file to disk.
569 * 1 : filename = File to write to.
570 * 2 : file = Data structure to write.
572 * Returns : JB_ERR_OK on success
573 * JB_ERR_FILE on error writing to file.
574 * JB_ERR_MEMORY on out of memory
576 *********************************************************************/
577 jb_err edit_write_file(struct editable_file * file)
580 struct file_line * cur_line;
581 struct stat statbuf[1];
582 char version_buf[22]; /* 22 = ceil(log10(2^64)) + 2 = max number of
583 digits in time_t, assuming this is a 64-bit
584 machine, plus null terminator, plus one
588 assert(file->filename);
590 if (NULL == (fp = fopen(file->filename, "wt")))
595 cur_line = file->lines;
596 while (cur_line != NULL)
600 if (fputs(cur_line->raw, fp) < 0)
608 if (cur_line->prefix)
610 if (fputs(cur_line->prefix, fp) < 0)
616 if (cur_line->unprocessed)
618 if (fputs(cur_line->unprocessed, fp) < 0)
623 if (fputs("\n", fp) < 0)
631 /* FIXME: Write data from file->data->whatever */
635 cur_line = cur_line->next;
641 /* Update the version stamp in the file structure, since we just
642 * wrote to the file & changed it's date.
644 if (stat(file->filename, statbuf) < 0)
646 /* Error, probably file not found. */
649 file->version = (unsigned)statbuf->st_mtime;
651 /* Correct file->version_str */
652 freez(file->version_str);
653 snprintf(version_buf, 22, "%u", file->version);
654 version_buf[21] = '\0';
655 file->version_str = strdup(version_buf);
656 if (version_buf == NULL)
658 return JB_ERR_MEMORY;
665 /*********************************************************************
667 * Function : edit_free_file
669 * Description : Free a complete file in memory.
672 * 1 : file = Data structure to free.
676 *********************************************************************/
677 void edit_free_file(struct editable_file * file)
681 /* Silently ignore NULL pointer */
685 edit_free_file_lines(file->lines);
686 freez(file->filename);
687 freez(file->identifier);
688 freez(file->version_str);
690 file->parse_error_text = NULL; /* Statically allocated */
691 file->parse_error = NULL;
697 /*********************************************************************
699 * Function : edit_free_file
701 * Description : Free an entire linked list of file lines.
704 * 1 : first_line = Data structure to free.
708 *********************************************************************/
709 static void edit_free_file_lines(struct file_line * first_line)
711 struct file_line * next_line;
713 while (first_line != NULL)
715 next_line = first_line->next;
716 first_line->next = NULL;
717 freez(first_line->raw);
718 freez(first_line->prefix);
719 freez(first_line->unprocessed);
720 switch(first_line->type)
722 case 0: /* special case if memory zeroed */
723 case FILE_LINE_UNPROCESSED:
724 case FILE_LINE_BLANK:
725 case FILE_LINE_ALIAS_HEADER:
726 case FILE_LINE_SETTINGS_HEADER:
727 case FILE_LINE_DESCRIPTION_HEADER:
728 case FILE_LINE_DESCRIPTION_ENTRY:
729 case FILE_LINE_ALIAS_ENTRY:
731 /* No data is stored for these */
734 case FILE_LINE_ACTION:
735 free_action(first_line->data.action);
738 case FILE_LINE_SETTINGS_ENTRY:
739 freez(first_line->data.setting.name);
740 freez(first_line->data.setting.svalue);
743 /* Should never happen */
747 first_line->type = 0; /* paranoia */
749 first_line = next_line;
754 /*********************************************************************
756 * Function : match_actions_file_header_line
758 * Description : Match an actions file {{header}} line
761 * 1 : line - String from file
762 * 2 : name - Header to match against
764 * Returns : 0 iff they match.
766 *********************************************************************/
767 static int match_actions_file_header_line(const char * line, const char * name)
775 if ((line[0] != '{') || (line[1] != '{'))
781 /* Look for optional whitespace */
782 while ( (*line == ' ') || (*line == '\t') )
787 /* Look for the specified name (case-insensitive) */
789 if (0 != strncmpic(line, name, len))
795 /* Look for optional whitespace */
796 while ( (*line == ' ') || (*line == '\t') )
801 /* Look for "}}" and end of string*/
802 if ((line[0] != '}') || (line[1] != '}') || (line[2] != '\0'))
812 /*********************************************************************
814 * Function : match_actions_file_header_line
816 * Description : Match an actions file {{header}} line
819 * 1 : line - String from file. Must not start with
820 * whitespace (else infinite loop!)
821 * 2 : name - Destination for name
822 * 2 : name - Destination for value
824 * Returns : JB_ERR_OK on success
825 * JB_ERR_MEMORY on out-of-memory
826 * JB_ERR_PARSE if there's no "=" sign, or if there's
827 * nothing before the "=" sign (but empty
828 * values *after* the "=" sign are legal).
830 *********************************************************************/
831 static jb_err split_line_on_equals(const char * line, char ** pname, char ** pvalue)
833 const char * name_end;
834 const char * value_start;
840 assert(*line != ' ');
841 assert(*line != '\t');
846 value_start = strchr(line, '=');
847 if ((value_start == NULL) || (value_start == line))
852 name_end = value_start - 1;
854 /* Eat any whitespace before the '=' */
855 while ((*name_end == ' ') || (*name_end == '\t'))
858 * we already know we must have at least 1 non-ws char
859 * at start of buf - no need to check
864 name_len = name_end - line + 1; /* Length excluding \0 */
865 if (NULL == (*pname = (char *) malloc(name_len + 1)))
867 return JB_ERR_MEMORY;
869 strncpy(*pname, line, name_len);
870 (*pname)[name_len] = '\0';
872 /* Eat any the whitespace after the '=' */
874 while ((*value_start == ' ') || (*value_start == '\t'))
879 if (NULL == (*pvalue = strdup(value_start)))
883 return JB_ERR_MEMORY;
890 /*********************************************************************
892 * Function : edit_parse_actions_file
894 * Description : Parse an actions file in memory.
896 * Passed linked list must have the "data" member
897 * zeroed, and must contain valid "next" and
898 * "unprocessed" fields. The "raw" and "prefix"
899 * fields are ignored, and "type" is just overwritten.
901 * Note that on error the file may have been
905 * 1 : file = Actions file to be parsed in-place.
907 * Returns : JB_ERR_OK on success
908 * JB_ERR_MEMORY on out-of-memory
909 * JB_ERR_PARSE on error
911 *********************************************************************/
912 jb_err edit_parse_actions_file(struct editable_file * file)
914 struct file_line * cur_line;
916 const char * text; /* Text from a line */
917 char * name; /* For lines of the form name=value */
918 char * value; /* For lines of the form name=value */
919 struct action_alias * alias_list = NULL;
920 jb_err err = JB_ERR_OK;
922 /* alias_list contains the aliases defined in this file.
923 * It might be better to use the "file_line.data" fields
924 * in the relavent places instead.
927 cur_line = file->lines;
929 /* A note about blank line support: Blank lines should only
930 * ever occur as the last line in the file. This function
931 * is more forgiving than that - FILE_LINE_BLANK can occur
935 /* Skip leading blanks. Should only happen if file is
936 * empty (which is valid, but pointless).
938 while ( (cur_line != NULL)
939 && (cur_line->unprocessed[0] == '\0') )
942 cur_line->type = FILE_LINE_BLANK;
943 cur_line = cur_line->next;
946 if ( (cur_line != NULL)
947 && (cur_line->unprocessed[0] != '{') )
949 /* File doesn't start with a header */
950 file->parse_error = cur_line;
951 file->parse_error_text = "First (non-comment) line of the file must contain a header.";
955 if ( (cur_line != NULL) && (0 ==
956 match_actions_file_header_line(cur_line->unprocessed, "settings") ) )
958 cur_line->type = FILE_LINE_SETTINGS_HEADER;
960 cur_line = cur_line->next;
961 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
963 if (cur_line->unprocessed[0])
965 cur_line->type = FILE_LINE_SETTINGS_ENTRY;
967 err = split_line_on_equals(cur_line->unprocessed,
968 &cur_line->data.setting.name,
969 &cur_line->data.setting.svalue);
970 if (err == JB_ERR_MEMORY)
974 else if (err != JB_ERR_OK)
976 /* Line does not contain a name=value pair */
977 file->parse_error = cur_line;
978 file->parse_error_text = "Expected a name=value pair on this {{description}} line, but couldn't find one.";
984 cur_line->type = FILE_LINE_BLANK;
986 cur_line = cur_line->next;
990 if ( (cur_line != NULL) && (0 ==
991 match_actions_file_header_line(cur_line->unprocessed, "description") ) )
993 cur_line->type = FILE_LINE_DESCRIPTION_HEADER;
995 cur_line = cur_line->next;
996 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
998 if (cur_line->unprocessed[0])
1000 cur_line->type = FILE_LINE_DESCRIPTION_ENTRY;
1004 cur_line->type = FILE_LINE_BLANK;
1006 cur_line = cur_line->next;
1010 if ( (cur_line != NULL) && (0 ==
1011 match_actions_file_header_line(cur_line->unprocessed, "alias") ) )
1013 cur_line->type = FILE_LINE_ALIAS_HEADER;
1015 cur_line = cur_line->next;
1016 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1018 if (cur_line->unprocessed[0])
1020 /* define an alias */
1021 struct action_alias * new_alias;
1023 cur_line->type = FILE_LINE_ALIAS_ENTRY;
1025 err = split_line_on_equals(cur_line->unprocessed, &name, &value);
1026 if (err == JB_ERR_MEMORY)
1030 else if (err != JB_ERR_OK)
1032 /* Line does not contain a name=value pair */
1033 file->parse_error = cur_line;
1034 file->parse_error_text = "Expected a name=value pair on this {{alias}} line, but couldn't find one.";
1035 return JB_ERR_PARSE;
1038 if ((new_alias = zalloc(sizeof(*new_alias))) == NULL)
1043 free_alias_list(alias_list);
1044 return JB_ERR_MEMORY;
1047 err = get_actions(value, alias_list, new_alias->action);
1050 /* Invalid action or out of memory */
1054 free_alias_list(alias_list);
1055 if (err == JB_ERR_MEMORY)
1061 /* Line does not contain a name=value pair */
1062 file->parse_error = cur_line;
1063 file->parse_error_text = "This alias does not specify a valid set of actions.";
1064 return JB_ERR_PARSE;
1070 new_alias->name = name;
1073 new_alias->next = alias_list;
1074 alias_list = new_alias;
1078 cur_line->type = FILE_LINE_BLANK;
1080 cur_line = cur_line->next;
1084 /* Header done, process the main part of the file */
1085 while (cur_line != NULL)
1087 /* At this point, (cur_line->unprocessed[0] == '{') */
1088 assert(cur_line->unprocessed[0] == '{');
1089 text = cur_line->unprocessed + 1;
1090 len = strlen(text) - 1;
1091 if (text[len] != '}')
1093 /* No closing } on header */
1094 free_alias_list(alias_list);
1095 file->parse_error = cur_line;
1096 file->parse_error_text = "Headers starting with '{' must have a "
1097 "closing bracket ('}'). Headers starting with two brackets ('{{') "
1098 "must close with two brackets ('}}').";
1099 return JB_ERR_PARSE;
1104 /* An invalid {{ header. */
1105 free_alias_list(alias_list);
1106 file->parse_error = cur_line;
1107 file->parse_error_text = "Unknown or unexpected two-bracket header. "
1108 "Please remember that the system (two-bracket) headers must "
1109 "appear in the order {{settings}}, {{description}}, {{alias}}, "
1110 "and must appear before any actions (one-bracket) headers. "
1111 "Also note that system headers may not be repeated.";
1112 return JB_ERR_PARSE;
1115 while ( (*text == ' ') || (*text == '\t') )
1121 && ( (text[len - 1] == ' ')
1122 || (text[len - 1] == '\t') ) )
1127 cur_line->type = FILE_LINE_ACTION;
1129 /* Remove {} and make copy */
1130 if (NULL == (value = (char *) malloc(len + 1)))
1133 free_alias_list(alias_list);
1134 return JB_ERR_MEMORY;
1136 strncpy(value, text, len);
1140 err = get_actions(value, alias_list, cur_line->data.action);
1143 /* Invalid action or out of memory */
1145 free_alias_list(alias_list);
1146 if (err == JB_ERR_MEMORY)
1152 /* Line does not contain a name=value pair */
1153 file->parse_error = cur_line;
1154 file->parse_error_text = "This header does not specify a valid set of actions.";
1155 return JB_ERR_PARSE;
1159 /* Done with string - it was clobbered anyway */
1162 /* Process next line */
1163 cur_line = cur_line->next;
1165 /* Loop processing URL patterns */
1166 while ((cur_line != NULL) && (cur_line->unprocessed[0] != '{'))
1168 if (cur_line->unprocessed[0])
1170 /* Could parse URL here, but this isn't currently needed */
1172 cur_line->type = FILE_LINE_URL;
1176 cur_line->type = FILE_LINE_BLANK;
1178 cur_line = cur_line->next;
1180 } /* End main while(cur_line != NULL) loop */
1182 free_alias_list(alias_list);
1188 /*********************************************************************
1190 * Function : edit_read_file_lines
1192 * Description : Read all the lines of a file into memory.
1193 * Handles whitespace, comments and line continuation.
1196 * 1 : fp = File to read from. On return, this will be
1197 * at EOF but it will not have been closed.
1198 * 2 : pfile = Destination for a linked list of file_lines.
1199 * Will be set to NULL on error.
1201 * Returns : JB_ERR_OK on success
1202 * JB_ERR_MEMORY on out-of-memory
1204 *********************************************************************/
1205 jb_err edit_read_file_lines(FILE *fp, struct file_line ** pfile)
1207 struct file_line * first_line; /* Keep for return value or to free */
1208 struct file_line * cur_line; /* Current line */
1209 struct file_line * prev_line; /* Entry with prev_line->next = cur_line */
1217 cur_line = first_line = zalloc(sizeof(struct file_line));
1218 if (cur_line == NULL)
1220 return JB_ERR_MEMORY;
1223 cur_line->type = FILE_LINE_UNPROCESSED;
1225 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed);
1228 /* Out of memory or empty file. */
1229 /* Note that empty file is not an error we propogate up */
1231 return ((rval == JB_ERR_FILE) ? JB_ERR_OK : rval);
1236 prev_line = cur_line;
1237 cur_line = prev_line->next = zalloc(sizeof(struct file_line));
1238 if (cur_line == NULL)
1241 edit_free_file_lines(first_line);
1242 return JB_ERR_MEMORY;
1245 cur_line->type = FILE_LINE_UNPROCESSED;
1247 rval = edit_read_line(fp, &cur_line->raw, &cur_line->prefix, &cur_line->unprocessed);
1248 if ((rval != JB_ERR_OK) && (rval != JB_ERR_FILE))
1251 edit_free_file_lines(first_line);
1252 return JB_ERR_MEMORY;
1256 while (rval != JB_ERR_FILE);
1260 /* We allocated one too many - free it */
1261 prev_line->next = NULL;
1264 *pfile = first_line;
1269 /*********************************************************************
1271 * Function : edit_read_file
1273 * Description : Read a complete file into memory.
1274 * Handles CGI parameter parsing. If requested, also
1275 * checks the file's modification timestamp.
1278 * 1 : csp = Current client state (buffers, headers, etc...)
1279 * 2 : parameters = map of cgi parameters.
1280 * 3 : require_version = true to check "ver" parameter.
1281 * 4 : suffix = File extension, e.g. ".action".
1282 * 5 : pfile = Destination for the file. Will be set
1286 * filename : The name of the file to read, without the
1287 * path or ".action" extension.
1288 * ver : (Only if require_version is nonzero)
1289 * Timestamp of the actions file. If wrong, this
1290 * function fails with JB_ERR_MODIFIED.
1292 * Returns : JB_ERR_OK on success
1293 * JB_ERR_MEMORY on out-of-memory
1294 * JB_ERR_CGI_PARAMS if "filename" was not specified
1296 * JB_ERR_FILE if the file cannot be opened or
1298 * JB_ERR_MODIFIED if version checking was requested and
1299 * failed - the file was modified outside
1300 * of this CGI editor instance.
1302 *********************************************************************/
1303 jb_err edit_read_file(struct client_state *csp,
1304 const struct map *parameters,
1305 int require_version,
1307 struct editable_file **pfile)
1309 struct file_line * lines;
1313 const char * identifier;
1314 struct editable_file * file;
1315 unsigned version = 0;
1316 struct stat statbuf[1];
1317 char version_buf[22];
1325 err = get_file_name_param(csp, parameters, "filename", suffix,
1326 &filename, &identifier);
1332 if (stat(filename, statbuf) < 0)
1334 /* Error, probably file not found. */
1338 version = (unsigned) statbuf->st_mtime;
1340 if (require_version)
1342 unsigned specified_version;
1343 err = get_number_param(csp, parameters, "ver", &specified_version);
1350 if (version != specified_version)
1352 return JB_ERR_MODIFIED;
1356 if (NULL == (fp = fopen(filename,"rt")))
1362 err = edit_read_file_lines(fp, &lines);
1372 file = (struct editable_file *) zalloc(sizeof(*file));
1376 edit_free_file_lines(lines);
1380 file->lines = lines;
1381 file->filename = filename;
1382 file->version = version;
1383 file->identifier = strdup(identifier);
1385 if (file->identifier == NULL)
1387 edit_free_file(file);
1388 return JB_ERR_MEMORY;
1391 /* Correct file->version_str */
1392 freez(file->version_str);
1393 snprintf(version_buf, 22, "%u", file->version);
1394 version_buf[21] = '\0';
1395 file->version_str = strdup(version_buf);
1396 if (version_buf == NULL)
1398 edit_free_file(file);
1399 return JB_ERR_MEMORY;
1407 /*********************************************************************
1409 * Function : edit_read_actions_file
1411 * Description : Read a complete actions file into memory.
1412 * Handles CGI parameter parsing. If requested, also
1413 * checks the file's modification timestamp.
1415 * If this function detects an error in the categories
1416 * JB_ERR_FILE, JB_ERR_MODIFIED, or JB_ERR_PARSE,
1417 * then it handles it by filling in the specified
1418 * response structure and returning JB_ERR_FILE.
1421 * 1 : csp = Current client state (buffers, headers, etc...)
1422 * 2 : rsp = HTTP response. Only filled in on error.
1423 * 2 : parameters = map of cgi parameters.
1424 * 3 : require_version = true to check "ver" parameter.
1425 * 4 : pfile = Destination for the file. Will be set
1429 * filename : The name of the actions file to read, without the
1430 * path or ".action" extension.
1431 * ver : (Only if require_version is nonzero)
1432 * Timestamp of the actions file. If wrong, this
1433 * function fails with JB_ERR_MODIFIED.
1435 * Returns : JB_ERR_OK on success
1436 * JB_ERR_MEMORY on out-of-memory
1437 * JB_ERR_CGI_PARAMS if "filename" was not specified
1439 * JB_ERR_FILE if the file does not contain valid data,
1440 * or if file cannot be opened or
1441 * contains no data, or if version
1442 * checking was requested and failed.
1444 *********************************************************************/
1445 jb_err edit_read_actions_file(struct client_state *csp,
1446 struct http_response *rsp,
1447 const struct map *parameters,
1448 int require_version,
1449 struct editable_file **pfile)
1452 struct editable_file *file;
1460 err = edit_read_file(csp, parameters, require_version, ".action", &file);
1463 /* Try to handle if possible */
1464 if (err == JB_ERR_FILE)
1466 err = cgi_error_file(csp, rsp, lookup(parameters, "filename"));
1468 else if (err == JB_ERR_MODIFIED)
1470 err = cgi_error_modified(csp, rsp, lookup(parameters, "filename"));
1472 if (err == JB_ERR_OK)
1475 * Signal to higher-level CGI code that there was a problem but we
1476 * handled it, they should just return JB_ERR_OK.
1483 err = edit_parse_actions_file(file);
1486 if (err == JB_ERR_PARSE)
1488 err = cgi_error_parse(csp, rsp, file);
1489 if (err == JB_ERR_OK)
1492 * Signal to higher-level CGI code that there was a problem but we
1493 * handled it, they should just return JB_ERR_OK.
1498 edit_free_file(file);
1507 /*********************************************************************
1509 * Function : get_file_name_param
1511 * Description : Get the name of the file to edit from the parameters
1512 * passed to a CGI function. This function handles
1513 * security checks such as blocking urls containing
1514 * "/" or ".", prepending the config file directory,
1515 * and adding the specified suffix.
1517 * (This is an essential security check, otherwise
1518 * users may be able to pass "../../../etc/passwd"
1519 * and overwrite the password file [linux], "prn:"
1520 * and print random data [Windows], etc...)
1522 * This function only allows filenames contining the
1523 * characters '-', '_', 'A'-'Z', 'a'-'z', and '0'-'9'.
1524 * That's probably too restrictive but at least it's
1528 * 1 : csp = Current client state (buffers, headers, etc...)
1529 * 2 : parameters = map of cgi parameters
1530 * 3 : suffix = File extension, e.g. ".actions"
1531 * 4 : pfilename = destination for full filename. Caller
1532 * free()s. Set to NULL on error.
1533 * 5 : pparam = destination for partial filename,
1534 * suitable for use in another URL. Allocated as part
1535 * of the map "parameters", so don't free it.
1536 * Set to NULL if not specified.
1538 * Returns : JB_ERR_OK on success
1539 * JB_ERR_MEMORY on out-of-memory
1540 * JB_ERR_CGI_PARAMS if "filename" was not specified
1543 *********************************************************************/
1544 static jb_err get_file_name_param(struct client_state *csp,
1545 const struct map *parameters,
1546 const char *param_name,
1549 const char **pparam)
1567 param = lookup(parameters, param_name);
1570 return JB_ERR_CGI_PARAMS;
1575 len = strlen(param);
1576 if (len >= FILENAME_MAX)
1579 return JB_ERR_CGI_PARAMS;
1582 /* Check every character to see if it's legal */
1584 while ((ch = *s++) != '\0')
1586 if ( ((ch < 'A') || (ch > 'Z'))
1587 && ((ch < 'a') || (ch > 'z'))
1588 && ((ch < '0') || (ch > '9'))
1592 /* Probable hack attempt. */
1593 return JB_ERR_CGI_PARAMS;
1597 /* Append extension */
1598 name = malloc(len + strlen(suffix) + 1);
1601 return JB_ERR_MEMORY;
1603 strcpy(name, param);
1604 strcpy(name + len, suffix);
1607 fullpath = make_path(csp->config->confdir, name);
1609 if (fullpath == NULL)
1611 return JB_ERR_MEMORY;
1615 *pfilename = fullpath;
1621 /*********************************************************************
1623 * Function : get_number_param
1625 * Description : Get a non-negative integer from the parameters
1626 * passed to a CGI function.
1629 * 1 : csp = Current client state (buffers, headers, etc...)
1630 * 2 : parameters = map of cgi parameters
1631 * 3 : name = Name of CGI parameter to read
1632 * 4 : pvalue = destination for value.
1633 * Set to -1 on error.
1635 * Returns : JB_ERR_OK on success
1636 * JB_ERR_MEMORY on out-of-memory
1637 * JB_ERR_CGI_PARAMS if the parameter was not specified
1640 *********************************************************************/
1641 static jb_err get_number_param(struct client_state *csp,
1642 const struct map *parameters,
1657 param = lookup(parameters, name);
1660 return JB_ERR_CGI_PARAMS;
1663 /* We don't use atoi because I want to check this carefully... */
1666 while ((ch = *param++) != '\0')
1668 if ((ch < '0') || (ch > '9'))
1670 return JB_ERR_CGI_PARAMS;
1677 * <limits.h> defines UINT_MAX
1679 * (UINT_MAX - ch) / 10 is the largest number that
1680 * can be safely multiplied by 10 then have ch added.
1682 if (value > ((UINT_MAX - (unsigned)ch) / 10U))
1684 return JB_ERR_CGI_PARAMS;
1687 value = value * 10 + ch;
1697 /*********************************************************************
1699 * Function : map_radio
1701 * Description : Map a set of radio button values. E.g. if you have
1702 * 3 radio buttons, declare them as:
1703 * <option type="radio" name="xyz" @xyz-a@>
1704 * <option type="radio" name="xyz" @xyz-b@>
1705 * <option type="radio" name="xyz" @xyz-c@>
1706 * Then map one of the @xyz-?@ variables to "checked"
1707 * and all the others to empty by calling:
1708 * map_radio(exports, "xyz", "abc", sel)
1709 * Where 'sel' is 'a', 'b', or 'c'.
1712 * 1 : exports = Exports map to modify.
1713 * 2 : optionname = name for map
1714 * 3 : values = null-terminated list of values;
1715 * 4 : value = Selected value.
1717 * CGI Parameters : None
1719 * Returns : JB_ERR_OK on success
1720 * JB_ERR_MEMORY on out-of-memory
1722 *********************************************************************/
1723 static jb_err map_radio(struct map * exports,
1724 const char * optionname,
1725 const char * values,
1737 len = strlen(optionname);
1738 buf = malloc(len + 3);
1741 return JB_ERR_MEMORY;
1744 strcpy(buf, optionname);
1749 while ((c = *values++) != '\0')
1754 if (map(exports, buf, 1, "", 1))
1757 return JB_ERR_MEMORY;
1763 if (map(exports, buf, 0, "checked", 1))
1766 return JB_ERR_MEMORY;
1773 /*********************************************************************
1775 * Function : actions_to_radio
1777 * Description : Converts a actionsfile entry into settings for
1778 * radio buttons and edit boxes on a HTML form.
1781 * 1 : exports = List of substitutions to add to.
1782 * 2 : action = Action to read
1784 * Returns : JB_ERR_OK on success
1785 * JB_ERR_MEMORY on out-of-memory
1787 *********************************************************************/
1788 static jb_err actions_to_radio(struct map * exports,
1789 const struct action_spec *action)
1791 unsigned mask = action->mask;
1792 unsigned add = action->add;
1800 mask = action->mask;
1803 /* sanity - prevents "-feature +feature" */
1807 #define DEFINE_ACTION_BOOL(name, bit) \
1808 if (!(mask & bit)) \
1810 current_mode = 'n'; \
1812 else if (add & bit) \
1814 current_mode = 'y'; \
1818 current_mode = 'x'; \
1820 if (map_radio(exports, name, "ynx", current_mode)) \
1822 return JB_ERR_MEMORY; \
1825 #define DEFINE_ACTION_STRING(name, bit, index) \
1826 DEFINE_ACTION_BOOL(name, bit); \
1829 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default) \
1832 checked = !strcmp(action->string[index], value); \
1836 checked = is_default; \
1838 mapped_param |= checked; \
1839 if (map(exports, name "-param-" value, 1, (checked ? "checked" : ""), 1)) \
1841 return JB_ERR_MEMORY; \
1844 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val) \
1845 if (map(exports, name "-param-custom", 1, \
1846 ((!mapped_param) ? "checked" : ""), 1)) \
1848 return JB_ERR_MEMORY; \
1850 if (map(exports, name "-param", 1, \
1851 (((add & bit) && !mapped_param) ? \
1852 action->string[index] : default_val), 1)) \
1854 return JB_ERR_MEMORY; \
1857 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val) \
1858 if (map(exports, name "-param", 1, \
1859 ((add & bit) ? action->string[index] : default_val), 1)) \
1861 return JB_ERR_MEMORY; \
1864 #define DEFINE_ACTION_MULTI(name, index) \
1865 if (action->multi_add[index]->first) \
1867 current_mode = 'y'; \
1869 else if (action->multi_remove_all[index]) \
1871 current_mode = 'n'; \
1873 else if (action->multi_remove[index]->first) \
1875 current_mode = 'y'; \
1879 current_mode = 'x'; \
1881 if (map_radio(exports, name, "ynx", current_mode)) \
1883 return JB_ERR_MEMORY; \
1886 #define DEFINE_ACTION_ALIAS 0 /* No aliases for output */
1888 #include "actionlist.h"
1890 #undef DEFINE_ACTION_MULTI
1891 #undef DEFINE_ACTION_STRING
1892 #undef DEFINE_ACTION_BOOL
1893 #undef DEFINE_ACTION_ALIAS
1894 #undef DEFINE_CGI_PARAM_CUSTOM
1895 #undef DEFINE_CGI_PARAM_RADIO
1896 #undef DEFINE_CGI_PARAM_NO_RADIO
1902 /*********************************************************************
1904 * Function : actions_from_radio
1906 * Description : Converts a map of parameters passed to a CGI function
1907 * into an actionsfile entry.
1910 * 1 : parameters = parameters to the CGI call
1911 * 2 : action = Action to change. Must be valid before
1912 * the call, actions not specified will be
1915 * Returns : JB_ERR_OK on success
1916 * JB_ERR_MEMORY on out-of-memory
1918 *********************************************************************/
1919 static jb_err actions_from_radio(const struct map * parameters,
1920 struct action_spec *action)
1929 #define DEFINE_ACTION_BOOL(name, bit) \
1930 if (NULL != (param = lookup(parameters, name))) \
1932 ch = toupper((int)param[0]); \
1935 action->add |= bit; \
1936 action->mask |= bit; \
1938 else if (ch == 'N') \
1940 action->add &= ~bit; \
1941 action->mask &= ~bit; \
1943 else if (ch == 'X') \
1945 action->add &= ~bit; \
1946 action->mask |= bit; \
1950 #define DEFINE_ACTION_STRING(name, bit, index) \
1951 if (NULL != (param = lookup(parameters, name))) \
1953 ch = toupper((int)param[0]); \
1956 param = lookup(parameters, name "-mode"); \
1957 if ((*param == '\0') || (0 == strcmp(param, "CUSTOM"))) \
1959 param = lookup(parameters, name "-param"); \
1961 if (*param != '\0') \
1963 if (NULL == (param_dup = strdup(param))) \
1965 return JB_ERR_MEMORY; \
1967 freez(action->string[index]); \
1968 action->add |= bit; \
1969 action->mask |= bit; \
1970 action->string[index] = param_dup; \
1973 else if (ch == 'N') \
1975 if (action->add & bit) \
1977 freez(action->string[index]); \
1979 action->add &= ~bit; \
1980 action->mask &= ~bit; \
1982 else if (ch == 'X') \
1984 if (action->add & bit) \
1986 freez(action->string[index]); \
1988 action->add &= ~bit; \
1989 action->mask |= bit; \
1993 #define DEFINE_ACTION_MULTI(name, index) \
1994 if (NULL != (param = lookup(parameters, name))) \
1996 ch = toupper((int)param[0]); \
2001 else if (ch == 'N') \
2003 list_remove_all(action->multi_add[index]); \
2004 list_remove_all(action->multi_remove[index]); \
2005 action->multi_remove_all[index] = 1; \
2007 else if (ch == 'X') \
2009 list_remove_all(action->multi_add[index]); \
2010 list_remove_all(action->multi_remove[index]); \
2011 action->multi_remove_all[index] = 0; \
2015 #define DEFINE_CGI_PARAM_CUSTOM(name, bit, index, default_val)
2016 #define DEFINE_CGI_PARAM_RADIO(name, bit, index, value, is_default)
2017 #define DEFINE_CGI_PARAM_NO_RADIO(name, bit, index, default_val)
2019 #define DEFINE_ACTION_ALIAS 0 /* No aliases for URL parsing */
2021 #include "actionlist.h"
2023 #undef DEFINE_ACTION_MULTI
2024 #undef DEFINE_ACTION_STRING
2025 #undef DEFINE_ACTION_BOOL
2026 #undef DEFINE_ACTION_ALIAS
2027 #undef DEFINE_CGI_PARAM_CUSTOM
2028 #undef DEFINE_CGI_PARAM_RADIO
2029 #undef DEFINE_CGI_PARAM_NO_RADIO
2035 /*********************************************************************
2037 * Function : cgi_error_modified
2039 * Description : CGI function that is called when a file is modified
2040 * outside the CGI editor.
2043 * 1 : csp = Current client state (buffers, headers, etc...)
2044 * 2 : rsp = http_response data structure for output
2045 * 3 : filename = The file that was modified.
2047 * CGI Parameters : none
2049 * Returns : JB_ERR_OK on success
2050 * JB_ERR_MEMORY on out-of-memory error.
2052 *********************************************************************/
2053 jb_err cgi_error_modified(struct client_state *csp,
2054 struct http_response *rsp,
2055 const char *filename)
2057 struct map *exports;
2064 if (NULL == (exports = default_exports(csp, NULL)))
2066 return JB_ERR_MEMORY;
2069 err = map(exports, "filename", 1, filename, 1);
2076 return template_fill_for_cgi(csp, "cgi-error-modified", exports, rsp);
2080 /*********************************************************************
2082 * Function : cgi_error_parse
2084 * Description : CGI function that is called when a file cannot
2085 * be parsed by the CGI editor.
2088 * 1 : csp = Current client state (buffers, headers, etc...)
2089 * 2 : rsp = http_response data structure for output
2090 * 3 : file = The file that was modified.
2092 * CGI Parameters : none
2094 * Returns : JB_ERR_OK on success
2095 * JB_ERR_MEMORY on out-of-memory error.
2097 *********************************************************************/
2098 jb_err cgi_error_parse(struct client_state *csp,
2099 struct http_response *rsp,
2100 struct editable_file *file)
2102 struct map *exports;
2104 struct file_line *cur_line;
2110 if (NULL == (exports = default_exports(csp, NULL)))
2112 return JB_ERR_MEMORY;
2115 err = map(exports, "filename", 1, file->identifier, 1);
2116 err = err || map(exports, "parse-error", 1, file->parse_error_text, 1);
2118 cur_line = file->parse_error;
2121 err = err || map(exports, "line-raw", 1, html_encode(cur_line->raw), 0);
2122 err = err || map(exports, "line-data", 1, html_encode(cur_line->unprocessed), 0);
2130 return template_fill_for_cgi(csp, "cgi-error-parse", exports, rsp);
2134 /*********************************************************************
2136 * Function : cgi_error_file
2138 * Description : CGI function that is called when a file cannot be
2139 * opened by the CGI editor.
2142 * 1 : csp = Current client state (buffers, headers, etc...)
2143 * 2 : rsp = http_response data structure for output
2144 * 3 : filename = The file that was modified.
2146 * CGI Parameters : none
2148 * Returns : JB_ERR_OK on success
2149 * JB_ERR_MEMORY on out-of-memory error.
2151 *********************************************************************/
2152 jb_err cgi_error_file(struct client_state *csp,
2153 struct http_response *rsp,
2154 const char *filename)
2156 struct map *exports;
2163 if (NULL == (exports = default_exports(csp, NULL)))
2165 return JB_ERR_MEMORY;
2168 err = map(exports, "filename", 1, filename, 1);
2175 return template_fill_for_cgi(csp, "cgi-error-file", exports, rsp);
2179 /*********************************************************************
2181 * Function : cgi_error_bad_param
2183 * Description : CGI function that is called if the parameters
2184 * (query string) for a CGI were wrong.
2187 * 1 : csp = Current client state (buffers, headers, etc...)
2188 * 2 : rsp = http_response data structure for output
2190 * CGI Parameters : none
2192 * Returns : JB_ERR_OK on success
2193 * JB_ERR_MEMORY on out-of-memory error.
2195 *********************************************************************/
2196 jb_err cgi_error_disabled(struct client_state *csp,
2197 struct http_response *rsp)
2199 struct map *exports;
2204 if (NULL == (exports = default_exports(csp, NULL)))
2206 return JB_ERR_MEMORY;
2209 return template_fill_for_cgi(csp, "cgi-error-disabled", exports, rsp);
2213 /*********************************************************************
2215 * Function : cgi_edit_actions
2217 * Description : CGI function that allows the user to choose which
2218 * actions file to edit.
2221 * 1 : csp = Current client state (buffers, headers, etc...)
2222 * 2 : rsp = http_response data structure for output
2223 * 3 : parameters = map of cgi parameters
2225 * CGI Parameters : None
2227 * Returns : JB_ERR_OK on success
2228 * JB_ERR_MEMORY on out-of-memory error
2230 *********************************************************************/
2231 jb_err cgi_edit_actions(struct client_state *csp,
2232 struct http_response *rsp,
2233 const struct map *parameters)
2236 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2238 return cgi_error_disabled(csp, rsp);
2241 /* FIXME: Incomplete */
2242 rsp->status = strdup("302 Local Redirect from Junkbuster");
2243 if (rsp->status == NULL)
2245 return JB_ERR_MEMORY;
2247 if (enlist_unique_header(rsp->headers, "Location", "http://ijbswa.sourceforge.net/config/edit-actions-list?filename=edit"))
2251 return JB_ERR_MEMORY;
2258 /*********************************************************************
2260 * Function : cgi_edit_actions_list
2262 * Description : CGI function that edits the actions list.
2263 * FIXME: This function shouldn't FATAL ever.
2264 * FIXME: This function doesn't check the retval of map()
2266 * 1 : csp = Current client state (buffers, headers, etc...)
2267 * 2 : rsp = http_response data structure for output
2268 * 3 : parameters = map of cgi parameters
2270 * CGI Parameters : filename
2272 * Returns : JB_ERR_OK on success
2273 * JB_ERR_MEMORY on out-of-memory
2274 * JB_ERR_FILE if the file cannot be opened or
2276 * JB_ERR_CGI_PARAMS if "filename" was not specified
2279 *********************************************************************/
2280 jb_err cgi_edit_actions_list(struct client_state *csp,
2281 struct http_response *rsp,
2282 const struct map *parameters)
2284 char * section_template;
2285 char * url_template;
2290 struct map * exports;
2291 struct map * section_exports;
2292 struct map * url_exports;
2293 struct editable_file * file;
2294 struct file_line * cur_line;
2295 int line_number = 0;
2299 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2301 return cgi_error_disabled(csp, rsp);
2304 err = edit_read_actions_file(csp, rsp, parameters, 0, &file);
2307 /* No filename specified, can't read file, or out of memory. */
2308 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2311 if (NULL == (exports = default_exports(csp, NULL)))
2313 edit_free_file(file);
2314 return JB_ERR_MEMORY;
2317 err = map(exports, "filename", 1, file->identifier, 1);
2318 err = err || map(exports, "ver", 1, file->version_str, 1);
2321 edit_free_file(file);
2326 /* Should do all global exports above this point */
2328 err = template_load(csp, §ion_template, "edit-actions-list-section");
2331 edit_free_file(file);
2333 if (err == JB_ERR_FILE)
2335 return cgi_error_no_template(csp, rsp, "edit-actions-list-section");
2340 err = template_load(csp, &url_template, "edit-actions-list-url");
2343 free(section_template);
2344 edit_free_file(file);
2346 if (err == JB_ERR_FILE)
2348 return cgi_error_no_template(csp, rsp, "edit-actions-list-url");
2353 err = template_fill(§ion_template, exports);
2357 edit_free_file(file);
2363 err = template_fill(&url_template, exports);
2366 free(section_template);
2367 edit_free_file(file);
2372 /* Find start of actions in file */
2373 cur_line = file->lines;
2375 while ((cur_line != NULL) && (cur_line->type != FILE_LINE_ACTION))
2377 cur_line = cur_line->next;
2381 if (NULL == (sections = strdup("")))
2383 free(section_template);
2385 edit_free_file(file);
2387 return JB_ERR_MEMORY;
2390 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_ACTION))
2392 if (NULL == (section_exports = new_map()))
2395 free(section_template);
2397 edit_free_file(file);
2399 return JB_ERR_MEMORY;
2402 snprintf(buf, 50, "%d", line_number);
2403 err = map(section_exports, "sectionid", 1, buf, 1);
2405 err = err || map(section_exports, "actions", 1,
2406 actions_to_html(cur_line->data.action), 0);
2408 if ((cur_line->next != NULL) && (cur_line->next->type == FILE_LINE_URL))
2410 /* This section contains at least one URL, don't allow delete */
2411 err = err || map_block_killer(section_exports, "empty-section");
2417 free(section_template);
2419 edit_free_file(file);
2421 free_map(section_exports);
2425 /* Should do all section-specific exports above this point */
2427 if (NULL == (urls = strdup("")))
2430 free(section_template);
2432 edit_free_file(file);
2434 free_map(section_exports);
2435 return JB_ERR_MEMORY;
2440 cur_line = cur_line->next;
2443 while ((cur_line != NULL) && (cur_line->type == FILE_LINE_URL))
2445 if (NULL == (url_exports = new_map()))
2449 free(section_template);
2451 edit_free_file(file);
2453 free_map(section_exports);
2454 return JB_ERR_MEMORY;
2457 snprintf(buf, 50, "%d", line_number);
2458 err = map(url_exports, "urlid", 1, buf, 1);
2460 snprintf(buf, 50, "%d", url_1_2);
2461 err = err || map(url_exports, "url-1-2", 1, buf, 1);
2463 err = err || map(url_exports, "url", 1,
2464 html_encode(cur_line->unprocessed), 0);
2470 free(section_template);
2472 edit_free_file(file);
2474 free_map(section_exports);
2475 free_map(url_exports);
2479 if (NULL == (s = strdup(url_template)))
2483 free(section_template);
2485 edit_free_file(file);
2487 free_map(section_exports);
2488 free_map(url_exports);
2489 return JB_ERR_MEMORY;
2492 err = template_fill(&s, section_exports);
2493 err = err || template_fill(&s, url_exports);
2494 err = err || string_append(&urls, s);
2495 free_map(url_exports);
2502 free(section_template);
2504 edit_free_file(file);
2506 free_map(section_exports);
2510 url_1_2 = 3 - url_1_2;
2512 cur_line = cur_line->next;
2516 err = map(section_exports, "urls", 1, urls, 0);
2521 free(section_template);
2523 edit_free_file(file);
2525 free_map(section_exports);
2529 /* Could also do section-specific exports here, but it wouldn't be as fast */
2531 if (NULL == (s = strdup(section_template)))
2534 free(section_template);
2536 edit_free_file(file);
2538 free_map(section_exports);
2539 return JB_ERR_MEMORY;
2542 err = template_fill(&s, section_exports);
2543 err = err || string_append(§ions, s);
2545 free_map(section_exports);
2550 free(section_template);
2552 edit_free_file(file);
2558 edit_free_file(file);
2559 free(section_template);
2562 err = map(exports, "sections", 1, sections, 0);
2569 /* Could also do global exports here, but it wouldn't be as fast */
2571 return template_fill_for_cgi(csp, "edit-actions-list", exports, rsp);
2575 /*********************************************************************
2577 * Function : cgi_edit_actions
2579 * Description : CGI function that edits the Actions list.
2582 * 1 : csp = Current client state (buffers, headers, etc...)
2583 * 2 : rsp = http_response data structure for output
2584 * 3 : parameters = map of cgi parameters
2586 * CGI Parameters : None
2588 * Returns : JB_ERR_OK on success
2589 * JB_ERR_MEMORY on out-of-memory
2590 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2591 * specified or not valid.
2593 *********************************************************************/
2594 jb_err cgi_edit_actions_for_url(struct client_state *csp,
2595 struct http_response *rsp,
2596 const struct map *parameters)
2598 struct map * exports;
2600 struct editable_file * file;
2601 struct file_line * cur_line;
2602 unsigned line_number;
2605 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2607 return cgi_error_disabled(csp, rsp);
2610 err = get_number_param(csp, parameters, "section", §ionid);
2616 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2619 /* No filename specified, can't read file, modified, or out of memory. */
2620 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2623 cur_line = file->lines;
2625 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2627 cur_line = cur_line->next;
2630 if ( (cur_line == NULL)
2631 || (line_number != sectionid)
2633 || (cur_line->type != FILE_LINE_ACTION))
2635 /* Invalid "sectionid" parameter */
2636 edit_free_file(file);
2637 return JB_ERR_CGI_PARAMS;
2640 if (NULL == (exports = default_exports(csp, NULL)))
2642 edit_free_file(file);
2643 return JB_ERR_MEMORY;
2646 err = map(exports, "filename", 1, file->identifier, 1);
2647 err = err || map(exports, "ver", 1, file->version_str, 1);
2648 err = err || map(exports, "section", 1, lookup(parameters, "section"), 1);
2650 err = err || actions_to_radio(exports, cur_line->data.action);
2652 edit_free_file(file);
2660 return template_fill_for_cgi(csp, "edit-actions-for-url", exports, rsp);
2664 /*********************************************************************
2666 * Function : cgi_edit_actions_submit
2668 * Description : CGI function that actually edits the Actions list.
2671 * 1 : csp = Current client state (buffers, headers, etc...)
2672 * 2 : rsp = http_response data structure for output
2673 * 3 : parameters = map of cgi parameters
2675 * CGI Parameters : None
2677 * Returns : JB_ERR_OK on success
2678 * JB_ERR_MEMORY on out-of-memory
2679 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2680 * specified or not valid.
2682 *********************************************************************/
2683 jb_err cgi_edit_actions_submit(struct client_state *csp,
2684 struct http_response *rsp,
2685 const struct map *parameters)
2691 struct editable_file * file;
2692 struct file_line * cur_line;
2697 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2699 return cgi_error_disabled(csp, rsp);
2702 err = get_number_param(csp, parameters, "section", §ionid);
2708 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2711 /* No filename specified, can't read file, modified, or out of memory. */
2712 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2715 cur_line = file->lines;
2717 for (line_number = 1; (cur_line != NULL) && (line_number < sectionid); line_number++)
2719 cur_line = cur_line->next;
2722 if ( (cur_line == NULL)
2723 || (line_number != sectionid)
2725 || (cur_line->type != FILE_LINE_ACTION))
2727 /* Invalid "sectionid" parameter */
2728 edit_free_file(file);
2729 return JB_ERR_CGI_PARAMS;
2732 err = actions_from_radio(parameters, cur_line->data.action);
2736 edit_free_file(file);
2740 if (NULL == (actiontext = actions_to_text(cur_line->data.action)))
2743 edit_free_file(file);
2744 return JB_ERR_MEMORY;
2747 len = strlen(actiontext);
2751 * Empty action - must special-case this.
2752 * Simply setting len to 1 is sufficient...
2757 if (NULL == (newtext = malloc(len + 2)))
2761 edit_free_file(file);
2762 return JB_ERR_MEMORY;
2764 strcpy(newtext, actiontext);
2768 newtext[len + 1] = '\0';
2770 freez(cur_line->raw);
2771 freez(cur_line->unprocessed);
2772 cur_line->unprocessed = newtext;
2774 err = edit_write_file(file);
2777 /* Error writing file */
2778 edit_free_file(file);
2782 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
2783 string_append(&target, file->identifier);
2785 edit_free_file(file);
2790 return JB_ERR_MEMORY;
2793 rsp->status = strdup("302 Local Redirect from Junkbuster");
2794 if (rsp->status == NULL)
2797 return JB_ERR_MEMORY;
2799 err = enlist_unique_header(rsp->headers, "Location", target);
2806 /*********************************************************************
2808 * Function : cgi_edit_actions_url
2810 * Description : CGI function that actually edits a URL pattern in
2814 * 1 : csp = Current client state (buffers, headers, etc...)
2815 * 2 : rsp = http_response data structure for output
2816 * 3 : parameters = map of cgi parameters
2819 * filename : Identifies the file to edit
2820 * ver : File's last-modified time
2821 * section : Line number of section to edit
2822 * pattern : Line number of pattern to edit
2823 * newval : New value for pattern
2825 * Returns : JB_ERR_OK on success
2826 * JB_ERR_MEMORY on out-of-memory
2827 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2828 * specified or not valid.
2830 *********************************************************************/
2831 jb_err cgi_edit_actions_url(struct client_state *csp,
2832 struct http_response *rsp,
2833 const struct map *parameters)
2837 const char * newval;
2839 struct editable_file * file;
2840 struct file_line * cur_line;
2841 unsigned line_number;
2845 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2847 return cgi_error_disabled(csp, rsp);
2850 err = get_number_param(csp, parameters, "section", §ionid);
2851 err = err || get_number_param(csp, parameters, "pattern", &patternid);
2857 newval = lookup(parameters, "newval");
2859 if ((*newval == '\0') || (sectionid < 1U) || (patternid < 1U))
2861 return JB_ERR_CGI_PARAMS;
2864 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
2867 /* No filename specified, can't read file, modified, or out of memory. */
2868 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
2872 cur_line = file->lines;
2874 while ((cur_line != NULL) && (line_number < sectionid))
2876 cur_line = cur_line->next;
2880 if ( (cur_line == NULL)
2881 || (cur_line->type != FILE_LINE_ACTION))
2883 /* Invalid "sectionid" parameter */
2884 edit_free_file(file);
2885 return JB_ERR_CGI_PARAMS;
2888 while (line_number < patternid)
2890 cur_line = cur_line->next;
2893 if ( (cur_line == NULL)
2894 || ( (cur_line->type != FILE_LINE_URL)
2895 && (cur_line->type != FILE_LINE_BLANK) ) )
2897 /* Invalid "patternid" parameter */
2898 edit_free_file(file);
2899 return JB_ERR_CGI_PARAMS;
2903 if (cur_line->type != FILE_LINE_URL)
2905 /* Invalid "patternid" parameter */
2906 edit_free_file(file);
2907 return JB_ERR_CGI_PARAMS;
2910 /* At this point, the line to edit is in cur_line */
2912 new_pattern = strdup(newval);
2913 if (NULL == new_pattern)
2915 edit_free_file(file);
2916 return JB_ERR_MEMORY;
2919 freez(cur_line->raw);
2920 freez(cur_line->unprocessed);
2921 cur_line->unprocessed = new_pattern;
2923 err = edit_write_file(file);
2926 /* Error writing file */
2927 edit_free_file(file);
2931 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
2932 string_append(&target, file->identifier);
2934 edit_free_file(file);
2939 return JB_ERR_MEMORY;
2942 rsp->status = strdup("302 Local Redirect from Junkbuster");
2943 if (rsp->status == NULL)
2946 return JB_ERR_MEMORY;
2948 err = enlist_unique_header(rsp->headers, "Location", target);
2955 /*********************************************************************
2957 * Function : cgi_edit_actions_add_url
2959 * Description : CGI function that actually adds a URL pattern to
2963 * 1 : csp = Current client state (buffers, headers, etc...)
2964 * 2 : rsp = http_response data structure for output
2965 * 3 : parameters = map of cgi parameters
2968 * filename : Identifies the file to edit
2969 * ver : File's last-modified time
2970 * section : Line number of section to edit
2971 * newval : New pattern
2973 * Returns : JB_ERR_OK on success
2974 * JB_ERR_MEMORY on out-of-memory
2975 * JB_ERR_CGI_PARAMS if the CGI parameters are not
2976 * specified or not valid.
2978 *********************************************************************/
2979 jb_err cgi_edit_actions_add_url(struct client_state *csp,
2980 struct http_response *rsp,
2981 const struct map *parameters)
2985 const char * newval;
2987 struct file_line * new_line;
2988 struct editable_file * file;
2989 struct file_line * cur_line;
2990 unsigned line_number;
2994 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
2996 return cgi_error_disabled(csp, rsp);
2999 err = get_number_param(csp, parameters, "section", §ionid);
3005 newval = lookup(parameters, "newval");
3007 if ((*newval == '\0') || (sectionid < 1U) || (patternid < 1U))
3009 return JB_ERR_CGI_PARAMS;
3012 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3015 /* No filename specified, can't read file, modified, or out of memory. */
3016 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3020 cur_line = file->lines;
3022 while ((cur_line != NULL) && (line_number < sectionid))
3024 cur_line = cur_line->next;
3028 if ( (cur_line == NULL)
3029 || (cur_line->type != FILE_LINE_ACTION))
3031 /* Invalid "sectionid" parameter */
3032 edit_free_file(file);
3033 return JB_ERR_CGI_PARAMS;
3036 /* At this point, the section header is in cur_line - add after this. */
3038 new_pattern = strdup(newval);
3039 if (NULL == new_pattern)
3041 edit_free_file(file);
3042 return JB_ERR_MEMORY;
3045 /* Allocate the new line */
3046 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3047 if (new_line == NULL)
3050 edit_free_file(file);
3051 return JB_ERR_MEMORY;
3054 /* Fill in the data members of the new line */
3055 new_line->raw = NULL;
3056 new_line->prefix = NULL;
3057 new_line->unprocessed = new_pattern;
3058 new_line->type = FILE_LINE_URL;
3060 /* Link new_line into the list, after cur_line */
3061 new_line->next = cur_line->next;
3062 cur_line->next = new_line;
3064 /* Done making changes, now commit */
3066 err = edit_write_file(file);
3069 /* Error writing file */
3070 edit_free_file(file);
3074 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3075 string_append(&target, file->identifier);
3077 edit_free_file(file);
3082 return JB_ERR_MEMORY;
3085 rsp->status = strdup("302 Local Redirect from Junkbuster");
3086 if (rsp->status == NULL)
3089 return JB_ERR_MEMORY;
3091 err = enlist_unique_header(rsp->headers, "Location", target);
3098 /*********************************************************************
3100 * Function : cgi_edit_actions_remove_url
3102 * Description : CGI function that actually removes a URL pattern from
3106 * 1 : csp = Current client state (buffers, headers, etc...)
3107 * 2 : rsp = http_response data structure for output
3108 * 3 : parameters = map of cgi parameters
3111 * filename : Identifies the file to edit
3112 * ver : File's last-modified time
3113 * section : Line number of section to edit
3114 * pattern : Line number of pattern to edit
3116 * Returns : JB_ERR_OK on success
3117 * JB_ERR_MEMORY on out-of-memory
3118 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3119 * specified or not valid.
3121 *********************************************************************/
3122 jb_err cgi_edit_actions_remove_url(struct client_state *csp,
3123 struct http_response *rsp,
3124 const struct map *parameters)
3128 struct editable_file * file;
3129 struct file_line * cur_line;
3130 struct file_line * prev_line;
3131 unsigned line_number;
3135 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3137 return cgi_error_disabled(csp, rsp);
3140 err = get_number_param(csp, parameters, "section", §ionid);
3141 err = err || get_number_param(csp, parameters, "pattern", &patternid);
3148 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3151 /* No filename specified, can't read file, modified, or out of memory. */
3152 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3156 cur_line = file->lines;
3158 while ((cur_line != NULL) && (line_number < sectionid))
3160 cur_line = cur_line->next;
3164 if ( (cur_line == NULL)
3165 || (cur_line->type != FILE_LINE_ACTION))
3167 /* Invalid "sectionid" parameter */
3168 edit_free_file(file);
3169 return JB_ERR_CGI_PARAMS;
3173 while (line_number < patternid)
3175 prev_line = cur_line;
3176 cur_line = cur_line->next;
3179 if ( (cur_line == NULL)
3180 || ( (cur_line->type != FILE_LINE_URL)
3181 && (cur_line->type != FILE_LINE_BLANK) ) )
3183 /* Invalid "patternid" parameter */
3184 edit_free_file(file);
3185 return JB_ERR_CGI_PARAMS;
3189 if (cur_line->type != FILE_LINE_URL)
3191 /* Invalid "patternid" parameter */
3192 edit_free_file(file);
3193 return JB_ERR_CGI_PARAMS;
3198 /* At this point, the line to remove is in cur_line, and the previous
3199 * one is in prev_line
3202 /* Unlink cur_line */
3203 prev_line->next = cur_line->next;
3204 cur_line->next = NULL;
3207 edit_free_file_lines(cur_line);
3209 err = edit_write_file(file);
3212 /* Error writing file */
3213 edit_free_file(file);
3217 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3218 string_append(&target, file->identifier);
3220 edit_free_file(file);
3225 return JB_ERR_MEMORY;
3228 rsp->status = strdup("302 Local Redirect from Junkbuster");
3229 if (rsp->status == NULL)
3232 return JB_ERR_MEMORY;
3234 err = enlist_unique_header(rsp->headers, "Location", target);
3241 /*********************************************************************
3243 * Function : cgi_edit_actions_section_remove
3245 * Description : CGI function that actually removes a whole section from
3246 * the actions file. The section must be empty first
3247 * (else JB_ERR_CGI_PARAMS).
3250 * 1 : csp = Current client state (buffers, headers, etc...)
3251 * 2 : rsp = http_response data structure for output
3252 * 3 : parameters = map of cgi parameters
3255 * filename : Identifies the file to edit
3256 * ver : File's last-modified time
3257 * section : Line number of section to edit
3258 * pattern : Line number of pattern to edit
3260 * Returns : JB_ERR_OK on success
3261 * JB_ERR_MEMORY on out-of-memory
3262 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3263 * specified or not valid.
3265 *********************************************************************/
3266 jb_err cgi_edit_actions_section_remove(struct client_state *csp,
3267 struct http_response *rsp,
3268 const struct map *parameters)
3271 struct editable_file * file;
3272 struct file_line * cur_line;
3273 struct file_line * prev_line;
3274 unsigned line_number;
3278 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3280 return cgi_error_disabled(csp, rsp);
3283 err = get_number_param(csp, parameters, "section", §ionid);
3289 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3292 /* No filename specified, can't read file, modified, or out of memory. */
3293 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3297 cur_line = file->lines;
3300 while ((cur_line != NULL) && (line_number < sectionid))
3302 prev_line = cur_line;
3303 cur_line = cur_line->next;
3307 if ( (cur_line == NULL)
3308 || (cur_line->type != FILE_LINE_ACTION) )
3310 /* Invalid "sectionid" parameter */
3311 edit_free_file(file);
3312 return JB_ERR_CGI_PARAMS;
3315 if ( (cur_line->next != NULL)
3316 && (cur_line->next->type == FILE_LINE_URL) )
3318 /* Section not empty. */
3319 edit_free_file(file);
3320 return JB_ERR_CGI_PARAMS;
3323 /* At this point, the line to remove is in cur_line, and the previous
3324 * one is in prev_line
3327 /* Unlink cur_line */
3328 if (prev_line == NULL)
3330 /* Removing the first line from the file */
3331 file->lines = cur_line->next;
3335 prev_line->next = cur_line->next;
3337 cur_line->next = NULL;
3340 edit_free_file_lines(cur_line);
3342 err = edit_write_file(file);
3345 /* Error writing file */
3346 edit_free_file(file);
3350 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3351 string_append(&target, file->identifier);
3353 edit_free_file(file);
3358 return JB_ERR_MEMORY;
3361 rsp->status = strdup("302 Local Redirect from Junkbuster");
3362 if (rsp->status == NULL)
3365 return JB_ERR_MEMORY;
3367 err = enlist_unique_header(rsp->headers, "Location", target);
3374 /*********************************************************************
3376 * Function : cgi_edit_actions_section_add
3378 * Description : CGI function that adds a new empty section to
3382 * 1 : csp = Current client state (buffers, headers, etc...)
3383 * 2 : rsp = http_response data structure for output
3384 * 3 : parameters = map of cgi parameters
3387 * filename : Identifies the file to edit
3388 * ver : File's last-modified time
3389 * section : Line number of section to add after, 0 for start
3392 * Returns : JB_ERR_OK on success
3393 * JB_ERR_MEMORY on out-of-memory
3394 * JB_ERR_CGI_PARAMS if the CGI parameters are not
3395 * specified or not valid.
3397 *********************************************************************/
3398 jb_err cgi_edit_actions_section_add(struct client_state *csp,
3399 struct http_response *rsp,
3400 const struct map *parameters)
3403 struct file_line * new_line;
3405 struct editable_file * file;
3406 struct file_line * cur_line;
3407 unsigned line_number;
3411 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_EDIT_ACTIONS))
3413 return cgi_error_disabled(csp, rsp);
3416 err = get_number_param(csp, parameters, "section", §ionid);
3422 err = edit_read_actions_file(csp, rsp, parameters, 1, &file);
3425 /* No filename specified, can't read file, modified, or out of memory. */
3426 return (err == JB_ERR_FILE ? JB_ERR_OK : err);
3430 cur_line = file->lines;
3434 /* Add to start of file */
3435 if (cur_line != NULL)
3437 /* There's something in the file, find the line before the first
3440 while ( (cur_line->next != NULL)
3441 && (cur_line->next->type != FILE_LINE_ACTION) )
3443 cur_line = cur_line->next;
3450 /* Add after stated section. */
3451 while ((cur_line != NULL) && (line_number < sectionid))
3453 cur_line = cur_line->next;
3457 if ( (cur_line == NULL)
3458 || (cur_line->type != FILE_LINE_ACTION))
3460 /* Invalid "sectionid" parameter */
3461 edit_free_file(file);
3462 return JB_ERR_CGI_PARAMS;
3465 /* Skip through the section to find the last line in it. */
3466 while ( (cur_line->next != NULL)
3467 && (cur_line->next->type != FILE_LINE_ACTION) )
3469 cur_line = cur_line->next;
3474 /* At this point, the last line in the previous section is in cur_line
3475 * - add after this. (Or if we need to add as the first line, cur_line
3479 new_text = strdup("{}");
3480 if (NULL == new_text)
3482 edit_free_file(file);
3483 return JB_ERR_MEMORY;
3486 /* Allocate the new line */
3487 new_line = (struct file_line *)zalloc(sizeof(*new_line));
3488 if (new_line == NULL)
3491 edit_free_file(file);
3492 return JB_ERR_MEMORY;
3495 /* Fill in the data members of the new line */
3496 new_line->raw = NULL;
3497 new_line->prefix = NULL;
3498 new_line->unprocessed = new_text;
3499 new_line->type = FILE_LINE_ACTION;
3501 if (cur_line != NULL)
3503 /* Link new_line into the list, after cur_line */
3504 new_line->next = cur_line->next;
3505 cur_line->next = new_line;
3509 /* Link new_line into the list, as first line */
3510 new_line->next = file->lines;
3511 file->lines = new_line;
3514 /* Done making changes, now commit */
3516 err = edit_write_file(file);
3519 /* Error writing file */
3520 edit_free_file(file);
3524 target = strdup("http://ijbswa.sourceforge.net/config/edit-actions-list?filename=");
3525 string_append(&target, file->identifier);
3527 edit_free_file(file);
3532 return JB_ERR_MEMORY;
3535 rsp->status = strdup("302 Local Redirect from Junkbuster");
3536 if (rsp->status == NULL)
3539 return JB_ERR_MEMORY;
3541 err = enlist_unique_header(rsp->headers, "Location", target);
3548 /*********************************************************************
3550 * Function : cgi_toggle
3552 * Description : CGI function that adds a new empty section to
3556 * 1 : csp = Current client state (buffers, headers, etc...)
3557 * 2 : rsp = http_response data structure for output
3558 * 3 : parameters = map of cgi parameters
3561 * set : If present, how to change toggle setting:
3562 * "enable", "disable", "toggle", or none (default).
3563 * mini : If present, use mini reply template.
3565 * Returns : JB_ERR_OK on success
3566 * JB_ERR_MEMORY on out-of-memory
3568 *********************************************************************/
3569 jb_err cgi_toggle(struct client_state *csp,
3570 struct http_response *rsp,
3571 const struct map *parameters)
3573 struct map *exports;
3575 const char *template_name;
3582 if (0 == (csp->config->feature_flags & RUNTIME_FEATURE_CGI_TOGGLE))
3584 return cgi_error_disabled(csp, rsp);
3587 if (NULL == (exports = default_exports(csp, "toggle")))
3589 return JB_ERR_MEMORY;
3592 mode = *(lookup(parameters, "set"));
3599 else if (mode == 'd')
3604 else if (mode == 't')
3607 g_bToggleIJB = !g_bToggleIJB;
3610 err = map_conditional(exports, "enabled", g_bToggleIJB);
3617 template_name = (*(lookup(parameters, "mini"))
3621 return template_fill_for_cgi(csp, template_name, exports, rsp);
3623 #endif /* def FEATURE_CGI_EDIT_ACTIONS */