1 /*****************************************************************************
2 * RRDtool 1.4.3 Copyright by Tobi Oetiker, 1997-2010
3 *****************************************************************************
4 * rrd_cgi.c RRD Web Page Generator
5 *****************************************************************************/
13 #define strcasecmp stricmp
14 #define strcasencmp strnicmp
18 /*#define DEBUG_PARSER
21 typedef struct var_s {
25 typedef struct cgi_s {
29 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
30 and replace by result of arg[2] call */
38 /**************************************************/
39 /* tag replacers ... they are called from parse */
40 /* through function pointers */
41 /**************************************************/
43 /* return cgi var named arg[0] */
48 /* return a quoted cgi var named arg[0] */
53 /* return a quoted and sanitized cgi variable */
58 /* call rrd_graph and insert appropriate image tag */
63 /* return PRINT functions from last rrd_graph call */
68 /* pretty-print the <last></last> value for some.rrd via strftime() */
73 /* pretty-print current time */
78 /* set an environment variable */
83 /* get an environment variable */
88 /* include the named file at this point */
93 /* for how long is the output of the cgi valid ? */
98 /* return rrdcgi version string */
110 /* format at-time specified times using strftime */
115 /** HTTP protocol needs special format, and GMT time **/
119 /* return a pointer to newly allocated copy of this string */
123 /* global variable for rrdcgi */
128 * Prints a valid CGI Header (Content-type...) etc.
133 /* rrdcgiDecodeString
134 * decode html escapes
137 char *rrdcgiDecodeString(
142 * Set/unsets debugging
150 * Reads in variables set via POST or stdin.
157 * Returns the value of the specified variable or NULL if it's empty
160 char *rrdcgiGetValue(
166 * Frees a list as returned by rrdcgiGetVariables()
173 * Frees the internal data structures
178 /* rrdcgiReadVariables()
180 * Read from stdin if no string is provided via CGI. Variables that
181 * doesn't have a value associated with it doesn't get stored.
183 s_var **rrdcgiReadVariables(
187 int rrdcgiDebugLevel = 0;
188 int rrdcgiDebugStderr = 1;
189 char *rrdcgiHeaderString = NULL;
190 char *rrdcgiType = NULL;
192 /* rrd interface to the variable functions {put,get}var() */
199 char *rrdsetvarconst(
204 /* variable store: put/get key-value pairs */
209 static const char *getvar(
210 const char *varname);
211 static const char *putvar(
216 /* key value pair that makes up an entry in the variable store */
218 int is_const; /* const variable or not */
219 const char *name; /* variable name */
220 const char *value; /* variable value */
223 /* the variable heap:
224 start with a heapsize of 10 variables */
225 #define INIT_VARSTORE_SIZE 10
226 static vardata *varheap = NULL;
227 static size_t varheap_size = 0;
229 /* allocate and initialize variable heap */
233 varheap = (vardata *) malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
234 if (varheap == NULL) {
235 fprintf(stderr, "ERROR: unable to initialize variable store\n");
238 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
239 varheap_size = INIT_VARSTORE_SIZE;
243 /* cleanup: free allocated memory */
250 for (i = 0; i < (int) varheap_size; i++) {
251 if (varheap[i].name) {
252 free((char *) varheap[i].name);
254 if (varheap[i].value) {
255 free((char *) varheap[i].value);
262 /* Get a variable from the variable store.
263 Return NULL in case the requested variable was not found. */
264 static const char *getvar(
269 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
270 if (0 == strcmp(name, varheap[i].name)) {
272 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
274 return varheap[i].value;
278 printf("<!-- getvar(%s) -> Not found-->\n", name);
283 /* Put a variable into the variable store. If a variable by that
284 name exists, it's value is overwritten with the new value unless it was
285 marked as 'const' (initialized by RRD::SETCONSTVAR).
286 Returns a copy the newly allocated value on success, NULL on error. */
287 static const char *putvar(
294 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
295 if (0 == strcmp(name, varheap[i].name)) {
296 /* overwrite existing entry */
297 if (varheap[i].is_const) {
299 printf("<!-- setver(%s, %s): not assigning: "
300 "const variable -->\n", name, value);
302 return varheap[i].value;
305 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
306 name, value, varheap[i].value);
308 /* make it possible to promote a variable to readonly */
309 varheap[i].is_const = is_const;
310 free((char *) varheap[i].value);
311 varheap[i].value = stralloc(value);
312 return varheap[i].value;
316 /* no existing variable found by that name, add it */
317 if (i == (int) varheap_size) {
318 /* ran out of heap: resize heap to double size */
319 size_t new_size = varheap_size * 2;
321 varheap = (vardata *) (realloc(varheap, sizeof(vardata) * new_size));
323 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
326 /* initialize newly allocated memory */ ;
327 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
328 varheap_size = new_size;
330 varheap[i].is_const = is_const;
331 varheap[i].name = stralloc(name);
332 varheap[i].value = stralloc(value);
335 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
337 return varheap[i].value;
340 /* expand those RRD:* directives that can be used recursivly */
341 static char *rrd_expand_vars(
347 printf("expanding variables in '%s'\n", buffer);
350 for (i = 0; buffer[i]; i++) {
351 if (buffer[i] != '<')
353 parse(&buffer, i, "<RRD::CV", cgiget);
354 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
355 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
356 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
357 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
358 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
359 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
360 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
361 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
366 static long goodfor = 0;
367 static char **calcpr = NULL;
374 for (i = 0; calcpr[i]; i++) {
386 /* create freeable version of the string */
395 nstr = malloc((strlen(str) + 1));
401 const char *file_name,
405 long writecnt = 0, totalcnt = MEMBLK;
410 if ((strcmp("-", file_name) == 0)) {
413 if ((input = fopen(file_name, "rb")) == NULL) {
414 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
422 } while (c != '\n' && !feof(input));
424 if (strcmp("-", file_name)) {
425 fseek(input, 0, SEEK_END);
426 /* have extra space for detecting EOF without realloc */
427 totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
428 if (totalcnt < MEMBLK)
429 totalcnt = MEMBLK; /* sanitize */
430 fseek(input, offset * sizeof(char), SEEK_SET);
432 if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
433 perror("Allocate Buffer:");
438 fread((*buffer) + writecnt, 1,
439 (totalcnt - writecnt) * sizeof(char), input);
440 if (writecnt >= totalcnt) {
443 rrd_realloc((*buffer),
444 (totalcnt + 4) * sizeof(char))) == NULL) {
445 perror("Realloc Buffer:");
449 } while (!feof(input));
450 (*buffer)[writecnt] = '\0';
451 if (strcmp("-", file_name) != 0) {
463 char *server_url = NULL;
466 struct option long_options[] = {
467 {"filter", no_argument, 0, 'f'},
471 #ifdef MUST_DISABLE_SIGFPE
472 signal(SIGFPE, SIG_IGN);
474 #ifdef MUST_DISABLE_FPMASK
478 opterr = 0; /* initialize getopt */
480 /* what do we get for cmdline arguments?
482 printf("%d-'%s'\n",i,argv[i]); */
484 int option_index = 0;
487 opt = getopt_long(argc, argv, "f", long_options, &option_index);
497 printf("unknown commandline option '%s'\n", argv[optind - 1]);
504 rrdcgiArg = rrdcgiInit();
505 server_url = getenv("SERVER_URL");
508 /* make sure we have one extra argument,
509 if there are others, we do not care Apache gives several */
511 /* if ( (optind != argc-2
512 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
513 && optind != argc-1) { */
515 if (optind >= argc) {
516 fprintf(stderr, "ERROR: expected a filename\n");
519 length = readfile(argv[optind], &buffer, 1);
522 if (rrd_test_error()) {
523 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
527 /* initialize variable heap */
531 /* some fake header for testing */
532 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
536 /* expand rrd directives in buffer recursivly */
537 for (i = 0; buffer[i]; i++) {
538 if (buffer[i] != '<')
541 parse(&buffer, i, "<RRD::CV", cgiget);
542 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
543 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
544 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
546 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
547 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
548 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
549 parse(&buffer, i, "<RRD::INCLUDE", includefile);
550 parse(&buffer, i, "<RRD::PRINT", drawprint);
551 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
552 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
553 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
554 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
555 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
556 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
557 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
561 printf("Content-Type: text/html\n"
562 "Content-Length: %zd\n", strlen(buffer));
564 if (labs(goodfor) > 0) {
568 printf("Last-Modified: %s\n", http_time(&now));
569 now += labs(goodfor);
570 printf("Expires: %s\n", http_time(&now));
572 printf("Refresh: %ld\n", labs(goodfor));
579 printf("%s", buffer);
590 /* remove occurrences of .. this is a general measure to make
591 paths which came in via cgi do not go UP ... */
598 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
601 return stralloc("[ERROR: allocating setenv buffer]");
603 sprintf(xyz, "%s=%s", args[0], args[1]);
604 if (putenv(xyz) == -1) {
606 return stralloc("[ERROR: failed to do putenv]");
610 return stralloc("[ERROR: setenv failed because not enough "
611 "arguments were defined]");
614 /* rrd interface to the variable function putvar() */
620 const char *result = putvar(args[0], args[1], 0 /* not const */ );
623 /* setvar does not return the value set */
626 return stralloc("[ERROR: putvar failed]");
628 return stralloc("[ERROR: putvar failed because not enough arguments "
632 /* rrd interface to the variable function putvar() */
633 char *rrdsetvarconst(
638 const char *result = putvar(args[0], args[1], 1 /* const */ );
641 /* setvar does not return the value set */
644 return stralloc("[ERROR: putvar failed]");
646 return stralloc("[ERROR: putvar failed because not enough arguments "
658 return stralloc("[ERROR: getenv failed because it did not "
659 "get 1 argument only]");
661 envvar = getenv(args[0]);
663 return stralloc(envvar);
665 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
666 return stralloc(buf);
678 return stralloc("[ERROR: getvar failed because it did not "
679 "get 1 argument only]");
681 value = getvar(args[0]);
683 return stralloc(value);
685 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
686 return stralloc(buf);
695 goodfor = atol(args[0]);
697 return stralloc("[ERROR: goodfor expected 1 argument]");
701 return stralloc("[ERROR: goodfor value must not be 0]");
707 char *rrdgetinternal(
712 if (strcasecmp(args[0], "VERSION") == 0) {
713 return stralloc(PACKAGE_VERSION);
714 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
715 return stralloc(__DATE__ " " __TIME__);
717 return stralloc("[ERROR: internal unknown argument]");
720 return stralloc("[ERROR: internal expected 1 argument]");
724 /* Format start or end times using strftime. We always need both the
725 * start and end times, because, either might be relative to the other.
727 #define MAX_STRFTIME_SIZE 256
732 rrd_time_value_t start_tv, end_tv;
733 char *parsetime_error = NULL;
734 char formatted[MAX_STRFTIME_SIZE];
736 time_t start_tmp, end_tmp;
738 /* Make sure that we were given the right number of args */
740 rrd_set_error("wrong number of args %d", argc);
744 /* Init start and end time */
745 rrd_parsetime("end-24h", &start_tv);
746 rrd_parsetime("now", &end_tv);
748 /* Parse the start and end times we were given */
749 if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
750 rrd_set_error("start time: %s", parsetime_error);
753 if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
754 rrd_set_error("end time: %s", parsetime_error);
757 if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
761 /* Do we do the start or end */
762 if (strcasecmp(args[0], "START") == 0) {
763 the_tm = localtime(&start_tmp);
764 } else if (strcasecmp(args[0], "END") == 0) {
765 the_tm = localtime(&end_tmp);
767 rrd_set_error("start/end not found in '%s'", args[0]);
772 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
773 return (stralloc(formatted));
775 rrd_set_error("strftime failed");
787 const char *filename = args[0];
789 readfile(filename, &buffer, 0);
790 if (rrd_test_error()) {
791 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
793 sprintf(err, "[ERROR: %s]", rrd_get_error());
800 return stralloc("[ERROR: No Inclue file defined]");
804 /* make a copy of buf and replace open/close brackets with '_' */
813 /* make a copy of the buffer */
821 if (*p == '<' || *p == '>') {
834 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
842 for (c = buf; *c != '\0'; c++)
845 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
846 perror("Malloc Buffer");
867 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
870 /* remove occurrences of .. this is a general measure to make
871 paths which came in via cgi do not go UP ... */
883 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
886 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
891 buf2 = malloc(strlen(buf) + 1);
893 perror("cgigetqp(): Malloc Path Buffer");
901 /* prevent mallicious paths from entering the system */
902 if (p[0] == '.' && p[1] == '.') {
914 /* Make sure the path is relative, e.g. does not start with '/' */
929 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
931 return stralloc("[ERROR: not enough arguments for RRD::CV]");
943 for (i = 0; i < argc; i++)
944 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
947 args[argc++] = "--imginfo";
948 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
952 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
954 return stralloc(calcpr[0]);
956 if (rrd_test_error()) {
958 malloc((strlen(rrd_get_error()) +
959 DS_NAM_SIZE) * sizeof(char));
960 sprintf(err, "[ERROR: %s]", rrd_get_error());
972 if (argc == 1 && calcpr) {
975 while (calcpr[i] != NULL)
976 i++; /*determine number lines in calcpr */
977 if (atol(args[0]) < i - 1)
978 return stralloc(calcpr[atol(args[0]) + 1]);
980 return stralloc("[ERROR: RRD::PRINT argument error]");
994 return stralloc("[ERROR: allocating strftime buffer]");
996 /* not raising argc in step with args - 1 since the last argument
997 will be used below for strftime */
999 last = rrd_last(argc, (char **) args - 1);
1000 if (rrd_test_error()) {
1002 malloc((strlen(rrd_get_error()) +
1003 DS_NAM_SIZE) * sizeof(char));
1004 sprintf(err, "[ERROR: %s]", rrd_get_error());
1008 tm_last = *localtime(&last);
1009 strftime(buf, 254, args[1], &tm_last);
1012 return stralloc("[ERROR: expected <RRD::TIME::LAST file.rrd strftime-format>]");
1019 time_t now = time(NULL);
1026 return stralloc("[ERROR: allocating strftime buffer]");
1028 tm_now = *localtime(&now);
1029 strftime(buf, 254, args[0], &tm_now);
1033 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
1035 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
1038 /* Scan buffer until an unescaped '>' arives.
1039 * Update argument array with arguments found.
1040 * Return end cursor where parsing stopped, or NULL in case of failure.
1043 * To allow nested constructs, we call rrd_expand_vars() for arguments
1044 * that contain RRD::x directives. These introduce a small memory leak
1045 * since we have to stralloc the arguments the way parse() works.
1049 int *argument_count,
1052 char *getP; /* read cursor */
1053 char *putP; /* write cursor */
1054 char Quote; /* type of quote if in quoted string, 0 otherwise */
1055 int tagcount; /* open tag count */
1056 int in_arg; /* if we currently are parsing an argument or not */
1057 int argsz; /* argument array size */
1058 int curarg_contains_rrd_directives;
1060 /* local array of arguments while parsing */
1065 printf("<-- scanargs(%s) -->\n", line);
1069 *argument_count = 0;
1071 /* create initial argument array of char pointers */
1073 argv = (char **) malloc(argsz * sizeof(char *));
1079 /* skip leading blanks */
1080 while (isspace((int) *line)) {
1091 curarg_contains_rrd_directives = 0;
1093 /* start parsing 'line' for arguments */
1095 unsigned char c = *getP++;
1097 if (c == '>' && !Quote && !tagcount) {
1098 /* this is our closing tag, quit scanning */
1102 /* remove all special chars */
1109 if (Quote || tagcount) {
1110 /* copy quoted/tagged (=RRD expanded) string */
1112 } else if (in_arg) {
1113 /* end argument string */
1116 if (curarg_contains_rrd_directives) {
1118 rrd_expand_vars(stralloc(argv[argc - 1]));
1119 curarg_contains_rrd_directives = 0;
1124 case '"': /* Fall through */
1130 /* copy quoted string */
1135 /* reference start of argument string in argument array */
1136 argv[argc++] = putP;
1145 /* start new argument */
1146 argv[argc++] = putP;
1156 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1157 curarg_contains_rrd_directives = 1;
1164 /* check if our argument array is still large enough */
1165 if (argc == argsz) {
1166 /* resize argument array */
1168 argv = rrd_realloc(argv, argsz * sizeof(char *));
1169 if (*argv == NULL) {
1175 /* terminate last argument found */
1177 if (curarg_contains_rrd_directives) {
1178 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1184 printf("<-- arguments found [%d]\n", argc);
1185 for (n = 0; n < argc; n++) {
1186 printf("arg %02d: '%s'\n", n, argv[n]);
1190 printf("<!-- No arguments found -->\n");
1194 /* update caller's notion of the argument array and it's size */
1196 /* note this is a bit of a hack since the rrd_cgi code used to just put
1197 its arguments into a normal array starting at 0 ... since the rrd_*
1198 commands expect and argc/argv array we used to just shift everything
1199 by -1 ... this in turn exploded when a rrd_* function tried to print
1200 argv[0] ... hence we are now doing everything in argv style but hand
1201 over seemingly the old array ... but doing argv-1 will actually end
1202 up in a 'good' place now. */
1204 *arguments = argv+1;
1205 *argument_count = argc-1;
1211 /* Return new scanning cursor:
1212 pointer to char after closing bracket */
1218 * Parse(): scan current portion of buffer for given tag.
1219 * If found, parse tag arguments and call 'func' for it.
1220 * The result of func is inserted at the current position
1224 char **buf, /* buffer */
1225 long i, /* offset in buffer */
1226 char *tag, /* tag to handle */
1227 char * (*func) (long,
1228 const char **) /* function to call for 'tag' */
1231 /* the name of the vairable ... */
1238 size_t taglen = strlen(tag);
1240 /* Current position in buffer should start with 'tag' */
1241 if (strncmp((*buf) + i, tag, taglen) != 0) {
1244 /* .. and match exactly (a whitespace following 'tag') */
1245 if (!isspace(*((*buf) + i + taglen))) {
1249 printf("parse(): handling tag '%s'\n", tag);
1252 /* Scan for arguments following the tag;
1253 scanargs() puts \0 into *buf ... so after scanargs it is probably
1254 not a good time to use strlen on buf */
1255 end = scanargs((*buf) + i + taglen, &argc, &args);
1257 /* got arguments, call function for 'tag' with arguments */
1258 val = func(argc, (const char **) args);
1261 /* unable to parse arguments, undo 0-termination by scanargs */
1262 for (; argc > 0; argc--) {
1263 *((args[argc - 1]) - 1) = ' ';
1266 /* next call, try parsing at current offset +1 */
1267 end = (*buf) + i + 1;
1269 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1270 " Check original file. This may have been altered "
1271 "by parsing.]\n\n");
1274 /* remember offset where we have to continue parsing */
1275 end_offset = end - (*buf);
1279 valln = strlen(val);
1282 /* Optionally resize buffer to hold the replacement value:
1283 Calculating the new length of the buffer is simple. add current
1284 buffer pos (i) to length of string after replaced tag to length
1285 of replacement string and add 1 for the final zero ... */
1286 if (end - (*buf) < (i + valln)) {
1287 /* make sure we do not shrink the mallocd block */
1288 size_t newbufsize = i + strlen(end) + valln + 1;
1290 *buf = rrd_realloc(*buf, newbufsize);
1293 perror("Realoc buf:");
1298 /* Update new end pointer:
1299 make sure the 'end' pointer gets moved along with the
1300 buf pointer when realloc moves memory ... */
1301 end = (*buf) + end_offset;
1303 /* splice the variable:
1304 step 1. Shift pending data to make room for 'val' */
1305 memmove((*buf) + i + valln, end, strlen(end) + 1);
1307 /* step 2. Insert val */
1309 memmove((*buf) + i, val, valln);
1312 return (valln > 0 ? valln - 1 : valln);
1319 static char buf[60];
1321 tmptime = gmtime(now);
1322 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1330 printf("Content-type: %s\n", rrdcgiType);
1332 printf("Content-type: text/html\n");
1333 if (rrdcgiHeaderString)
1334 printf("%s", rrdcgiHeaderString);
1343 rrdcgiDebugLevel = level;
1345 rrdcgiDebugLevel = 0;
1347 rrdcgiDebugStderr = 0;
1349 rrdcgiDebugStderr = 1;
1352 char *rrdcgiDecodeString(
1357 for (cp = text, xp = text; *cp; cp++) {
1359 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1360 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1361 if (islower(*(cp + 1)))
1362 *(cp + 1) = toupper(*(cp + 1));
1363 if (islower(*(cp + 2)))
1364 *(cp + 2) = toupper(*(cp + 2));
1367 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1369 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1377 memset(xp, 0, cp - xp);
1381 /* rrdcgiReadVariables()
1383 * Read from stdin if no string is provided via CGI. Variables that
1384 * doesn't have a value associated with it doesn't get stored.
1386 s_var **rrdcgiReadVariables(
1392 char *cp, *ip, *esp, *sptr;
1397 cp = getenv("REQUEST_METHOD");
1398 ip = getenv("CONTENT_LENGTH");
1400 if (cp && !strcmp(cp, "POST")) {
1403 if ((line = (char *) malloc(length + 2)) == NULL)
1405 if (fgets(line, length + 1, stdin) == NULL)
1409 } else if (cp && !strcmp(cp, "GET")) {
1410 esp = getenv("QUERY_STRING");
1411 if (esp && strlen(esp)) {
1412 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1414 sprintf(line, "%s", esp);
1419 printf("(offline mode: enter name=value pairs on standard input)\n");
1420 memset(tmp, 0, sizeof(tmp));
1421 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1423 if (tmp[strlen(tmp) - 1] == '\n')
1424 tmp[strlen(tmp) - 1] = '&';
1426 length += strlen(tmp);
1427 len = (length + 1) * sizeof(char);
1428 if ((line = (char *) realloc(line, len)) == NULL)
1432 length = strlen(tmp);
1433 len = (length + 1) * sizeof(char);
1434 if ((line = (char *) malloc(len)) == NULL)
1436 memset(line, 0, len);
1440 memset(tmp, 0, sizeof(tmp));
1444 if (line[strlen(line) - 1] == '&')
1445 line[strlen(line) - 1] = '\0';
1449 * From now on all cgi variables are stored in the variable line
1450 * and look like foo=bar&foobar=barfoo&foofoo=
1453 if (rrdcgiDebugLevel > 0) {
1454 if (rrdcgiDebugStderr)
1455 fprintf(stderr, "Received cgi input: %s\n", line);
1458 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1462 for (cp = line; *cp; cp++)
1467 for (numargs = 1, cp = line; *cp; cp++)
1472 if (rrdcgiDebugLevel > 0) {
1473 if (rrdcgiDebugStderr)
1474 fprintf(stderr, "%d cgi variables found.\n", numargs);
1476 printf("%d cgi variables found.<br>\n", numargs);
1479 len = (numargs + 1) * sizeof(s_var *);
1480 if ((result = (s_var **) malloc(len)) == NULL)
1482 memset(result, 0, len);
1487 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1490 ip = cp + strlen(cp);
1492 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1504 /* try to find out if there's already such a variable */
1505 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1506 || !(strlen(result[k]->name) ==
1507 (size_t) (esp - cp))); k++);
1509 if (k == i) { /* No such variable yet */
1510 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1512 if ((result[i]->name =
1513 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1515 memset(result[i]->name, 0, esp - cp + 1);
1516 strncpy(result[i]->name, cp, esp - cp);
1518 if ((result[i]->value =
1519 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1521 memset(result[i]->value, 0, ip - esp + 1);
1522 strncpy(result[i]->value, cp, ip - esp);
1523 result[i]->value = rrdcgiDecodeString(result[i]->value);
1524 if (rrdcgiDebugLevel) {
1525 if (rrdcgiDebugStderr)
1526 fprintf(stderr, "%s: %s\n", result[i]->name,
1529 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1530 result[i]->name, result[i]->value);
1533 } else { /* There is already such a name, suppose a mutiple field */
1536 (strlen(result[k]->value) + (ip - esp) +
1538 if ((sptr = (char *) malloc(len)) == NULL)
1540 memset(sptr, 0, len);
1541 sprintf(sptr, "%s\n", result[k]->value);
1542 strncat(sptr, cp, ip - esp);
1543 free(result[k]->value);
1544 result[k]->value = rrdcgiDecodeString(sptr);
1554 * Read from stdin if no string is provided via CGI. Variables that
1555 * doesn't have a value associated with it doesn't get stored.
1563 vars = rrdcgiReadVariables();
1568 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1575 char *rrdcgiGetValue(
1581 if (!parms || !parms->vars)
1583 for (i = 0; parms->vars[i]; i++)
1584 if (!strcmp(name, parms->vars[i]->name)) {
1585 if (rrdcgiDebugLevel > 0) {
1586 if (rrdcgiDebugStderr)
1587 fprintf(stderr, "%s found as %s\n", name,
1588 parms->vars[i]->value);
1590 printf("%s found as %s<br>\n", name,
1591 parms->vars[i]->value);
1593 return parms->vars[i]->value;
1595 if (rrdcgiDebugLevel) {
1596 if (rrdcgiDebugStderr)
1597 fprintf(stderr, "%s not found\n", name);
1599 printf("%s not found<br>\n", name);
1604 void rrdcgiFreeList(
1609 for (i = 0; list[i] != NULL; i++)
1622 for (i = 0; parms->vars[i]; i++) {
1623 if (parms->vars[i]->name)
1624 free(parms->vars[i]->name);
1625 if (parms->vars[i]->value)
1626 free(parms->vars[i]->value);
1627 free(parms->vars[i]);
1633 if (rrdcgiHeaderString) {
1634 free(rrdcgiHeaderString);
1635 rrdcgiHeaderString = NULL;