1 /*****************************************************************************
2 * RRDtool 1.4.2 Copyright by Tobi Oetiker, 1997-2009
3 *****************************************************************************
4 * rrd_cgi.c RRD Web Page Generator
5 *****************************************************************************/
13 /*#define DEBUG_PARSER
16 typedef struct var_s {
20 typedef struct cgi_s {
24 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
25 and replace by result of arg[2] call */
33 /**************************************************/
34 /* tag replacers ... they are called from parse */
35 /* through function pointers */
36 /**************************************************/
38 /* return cgi var named arg[0] */
43 /* return a quoted cgi var named arg[0] */
48 /* return a quoted and sanitized cgi variable */
53 /* call rrd_graph and insert appropriate image tag */
58 /* return PRINT functions from last rrd_graph call */
63 /* pretty-print the <last></last> value for some.rrd via strftime() */
68 /* pretty-print current time */
73 /* set an environment variable */
78 /* get an environment variable */
83 /* include the named file at this point */
88 /* for how long is the output of the cgi valid ? */
93 /* return rrdcgi version string */
105 /* format at-time specified times using strftime */
110 /** HTTP protocol needs special format, and GMT time **/
114 /* return a pointer to newly allocated copy of this string */
118 /* global variable for rrdcgi */
123 * Prints a valid CGI Header (Content-type...) etc.
128 /* rrdcgiDecodeString
129 * decode html escapes
132 char *rrdcgiDecodeString(
137 * Set/unsets debugging
145 * Reads in variables set via POST or stdin.
152 * Returns the value of the specified variable or NULL if it's empty
155 char *rrdcgiGetValue(
161 * Frees a list as returned by rrdcgiGetVariables()
168 * Frees the internal data structures
173 /* rrdcgiReadVariables()
175 * Read from stdin if no string is provided via CGI. Variables that
176 * doesn't have a value associated with it doesn't get stored.
178 s_var **rrdcgiReadVariables(
182 int rrdcgiDebugLevel = 0;
183 int rrdcgiDebugStderr = 1;
184 char *rrdcgiHeaderString = NULL;
185 char *rrdcgiType = NULL;
187 /* rrd interface to the variable functions {put,get}var() */
194 char *rrdsetvarconst(
199 /* variable store: put/get key-value pairs */
204 static const char *getvar(
205 const char *varname);
206 static const char *putvar(
211 /* key value pair that makes up an entry in the variable store */
213 int is_const; /* const variable or not */
214 const char *name; /* variable name */
215 const char *value; /* variable value */
218 /* the variable heap:
219 start with a heapsize of 10 variables */
220 #define INIT_VARSTORE_SIZE 10
221 static vardata *varheap = NULL;
222 static size_t varheap_size = 0;
224 /* allocate and initialize variable heap */
228 varheap = (vardata *) malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
229 if (varheap == NULL) {
230 fprintf(stderr, "ERROR: unable to initialize variable store\n");
233 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
234 varheap_size = INIT_VARSTORE_SIZE;
238 /* cleanup: free allocated memory */
245 for (i = 0; i < (int) varheap_size; i++) {
246 if (varheap[i].name) {
247 free((char *) varheap[i].name);
249 if (varheap[i].value) {
250 free((char *) varheap[i].value);
257 /* Get a variable from the variable store.
258 Return NULL in case the requested variable was not found. */
259 static const char *getvar(
264 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
265 if (0 == strcmp(name, varheap[i].name)) {
267 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
269 return varheap[i].value;
273 printf("<!-- getvar(%s) -> Not found-->\n", name);
278 /* Put a variable into the variable store. If a variable by that
279 name exists, it's value is overwritten with the new value unless it was
280 marked as 'const' (initialized by RRD::SETCONSTVAR).
281 Returns a copy the newly allocated value on success, NULL on error. */
282 static const char *putvar(
289 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
290 if (0 == strcmp(name, varheap[i].name)) {
291 /* overwrite existing entry */
292 if (varheap[i].is_const) {
294 printf("<!-- setver(%s, %s): not assigning: "
295 "const variable -->\n", name, value);
297 return varheap[i].value;
300 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
301 name, value, varheap[i].value);
303 /* make it possible to promote a variable to readonly */
304 varheap[i].is_const = is_const;
305 free((char *) varheap[i].value);
306 varheap[i].value = stralloc(value);
307 return varheap[i].value;
311 /* no existing variable found by that name, add it */
312 if (i == (int) varheap_size) {
313 /* ran out of heap: resize heap to double size */
314 size_t new_size = varheap_size * 2;
316 varheap = (vardata *) (realloc(varheap, sizeof(vardata) * new_size));
318 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
321 /* initialize newly allocated memory */ ;
322 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
323 varheap_size = new_size;
325 varheap[i].is_const = is_const;
326 varheap[i].name = stralloc(name);
327 varheap[i].value = stralloc(value);
330 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
332 return varheap[i].value;
335 /* expand those RRD:* directives that can be used recursivly */
336 static char *rrd_expand_vars(
342 printf("expanding variables in '%s'\n", buffer);
345 for (i = 0; buffer[i]; i++) {
346 if (buffer[i] != '<')
348 parse(&buffer, i, "<RRD::CV", cgiget);
349 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
350 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
351 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
352 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
353 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
354 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
355 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
356 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
361 static long goodfor = 0;
362 static char **calcpr = NULL;
369 for (i = 0; calcpr[i]; i++) {
381 /* create freeable version of the string */
390 nstr = malloc((strlen(str) + 1));
396 const char *file_name,
400 long writecnt = 0, totalcnt = MEMBLK;
405 if ((strcmp("-", file_name) == 0)) {
408 if ((input = fopen(file_name, "rb")) == NULL) {
409 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
417 } while (c != '\n' && !feof(input));
419 if (strcmp("-", file_name)) {
420 fseek(input, 0, SEEK_END);
421 /* have extra space for detecting EOF without realloc */
422 totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
423 if (totalcnt < MEMBLK)
424 totalcnt = MEMBLK; /* sanitize */
425 fseek(input, offset * sizeof(char), SEEK_SET);
427 if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
428 perror("Allocate Buffer:");
433 fread((*buffer) + writecnt, 1,
434 (totalcnt - writecnt) * sizeof(char), input);
435 if (writecnt >= totalcnt) {
438 rrd_realloc((*buffer),
439 (totalcnt + 4) * sizeof(char))) == NULL) {
440 perror("Realloc Buffer:");
444 } while (!feof(input));
445 (*buffer)[writecnt] = '\0';
446 if (strcmp("-", file_name) != 0) {
458 char *server_url = NULL;
461 struct option long_options[] = {
462 {"filter", no_argument, 0, 'f'},
466 #ifdef MUST_DISABLE_SIGFPE
467 signal(SIGFPE, SIG_IGN);
469 #ifdef MUST_DISABLE_FPMASK
473 opterr = 0; /* initialize getopt */
475 /* what do we get for cmdline arguments?
477 printf("%d-'%s'\n",i,argv[i]); */
479 int option_index = 0;
482 opt = getopt_long(argc, argv, "f", long_options, &option_index);
492 printf("unknown commandline option '%s'\n", argv[optind - 1]);
499 rrdcgiArg = rrdcgiInit();
500 server_url = getenv("SERVER_URL");
503 /* make sure we have one extra argument,
504 if there are others, we do not care Apache gives several */
506 /* if ( (optind != argc-2
507 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
508 && optind != argc-1) { */
510 if (optind >= argc) {
511 fprintf(stderr, "ERROR: expected a filename\n");
514 length = readfile(argv[optind], &buffer, 1);
517 if (rrd_test_error()) {
518 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
522 /* initialize variable heap */
526 /* some fake header for testing */
527 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
531 /* expand rrd directives in buffer recursivly */
532 for (i = 0; buffer[i]; i++) {
533 if (buffer[i] != '<')
536 parse(&buffer, i, "<RRD::CV", cgiget);
537 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
538 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
539 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
541 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
542 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
543 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
544 parse(&buffer, i, "<RRD::INCLUDE", includefile);
545 parse(&buffer, i, "<RRD::PRINT", drawprint);
546 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
547 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
548 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
549 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
550 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
551 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
552 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
556 printf("Content-Type: text/html\n"
557 "Content-Length: %zd\n", strlen(buffer));
559 if (labs(goodfor) > 0) {
563 printf("Last-Modified: %s\n", http_time(&now));
564 now += labs(goodfor);
565 printf("Expires: %s\n", http_time(&now));
567 printf("Refresh: %ld\n", labs(goodfor));
574 printf("%s", buffer);
585 /* remove occurrences of .. this is a general measure to make
586 paths which came in via cgi do not go UP ... */
593 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
596 return stralloc("[ERROR: allocating setenv buffer]");
598 sprintf(xyz, "%s=%s", args[0], args[1]);
599 if (putenv(xyz) == -1) {
601 return stralloc("[ERROR: failed to do putenv]");
605 return stralloc("[ERROR: setenv failed because not enough "
606 "arguments were defined]");
609 /* rrd interface to the variable function putvar() */
615 const char *result = putvar(args[0], args[1], 0 /* not const */ );
618 /* setvar does not return the value set */
621 return stralloc("[ERROR: putvar failed]");
623 return stralloc("[ERROR: putvar failed because not enough arguments "
627 /* rrd interface to the variable function putvar() */
628 char *rrdsetvarconst(
633 const char *result = putvar(args[0], args[1], 1 /* const */ );
636 /* setvar does not return the value set */
639 return stralloc("[ERROR: putvar failed]");
641 return stralloc("[ERROR: putvar failed because not enough arguments "
653 return stralloc("[ERROR: getenv failed because it did not "
654 "get 1 argument only]");
656 envvar = getenv(args[0]);
658 return stralloc(envvar);
660 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
661 return stralloc(buf);
673 return stralloc("[ERROR: getvar failed because it did not "
674 "get 1 argument only]");
676 value = getvar(args[0]);
678 return stralloc(value);
680 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
681 return stralloc(buf);
690 goodfor = atol(args[0]);
692 return stralloc("[ERROR: goodfor expected 1 argument]");
696 return stralloc("[ERROR: goodfor value must not be 0]");
702 char *rrdgetinternal(
707 if (strcasecmp(args[0], "VERSION") == 0) {
708 return stralloc(PACKAGE_VERSION);
709 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
710 return stralloc(__DATE__ " " __TIME__);
712 return stralloc("[ERROR: internal unknown argument]");
715 return stralloc("[ERROR: internal expected 1 argument]");
719 /* Format start or end times using strftime. We always need both the
720 * start and end times, because, either might be relative to the other.
722 #define MAX_STRFTIME_SIZE 256
727 rrd_time_value_t start_tv, end_tv;
728 char *parsetime_error = NULL;
729 char formatted[MAX_STRFTIME_SIZE];
731 time_t start_tmp, end_tmp;
733 /* Make sure that we were given the right number of args */
735 rrd_set_error("wrong number of args %d", argc);
739 /* Init start and end time */
740 rrd_parsetime("end-24h", &start_tv);
741 rrd_parsetime("now", &end_tv);
743 /* Parse the start and end times we were given */
744 if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
745 rrd_set_error("start time: %s", parsetime_error);
748 if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
749 rrd_set_error("end time: %s", parsetime_error);
752 if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
756 /* Do we do the start or end */
757 if (strcasecmp(args[0], "START") == 0) {
758 the_tm = localtime(&start_tmp);
759 } else if (strcasecmp(args[0], "END") == 0) {
760 the_tm = localtime(&end_tmp);
762 rrd_set_error("start/end not found in '%s'", args[0]);
767 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
768 return (stralloc(formatted));
770 rrd_set_error("strftime failed");
782 const char *filename = args[0];
784 readfile(filename, &buffer, 0);
785 if (rrd_test_error()) {
786 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
788 sprintf(err, "[ERROR: %s]", rrd_get_error());
795 return stralloc("[ERROR: No Inclue file defined]");
799 /* make a copy of buf and replace open/close brackets with '_' */
808 /* make a copy of the buffer */
816 if (*p == '<' || *p == '>') {
829 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
837 for (c = buf; *c != '\0'; c++)
840 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
841 perror("Malloc Buffer");
862 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
865 /* remove occurrences of .. this is a general measure to make
866 paths which came in via cgi do not go UP ... */
878 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
881 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
886 buf2 = malloc(strlen(buf) + 1);
888 perror("cgigetqp(): Malloc Path Buffer");
896 /* prevent mallicious paths from entering the system */
897 if (p[0] == '.' && p[1] == '.') {
909 /* Make sure the path is relative, e.g. does not start with '/' */
924 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
926 return stralloc("[ERROR: not enough arguments for RRD::CV]");
938 for (i = 0; i < argc; i++)
939 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
942 args[argc++] = "--imginfo";
943 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
947 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
949 return stralloc(calcpr[0]);
951 if (rrd_test_error()) {
953 malloc((strlen(rrd_get_error()) +
954 DS_NAM_SIZE) * sizeof(char));
955 sprintf(err, "[ERROR: %s]", rrd_get_error());
967 if (argc == 1 && calcpr) {
970 while (calcpr[i] != NULL)
971 i++; /*determine number lines in calcpr */
972 if (atol(args[0]) < i - 1)
973 return stralloc(calcpr[atol(args[0]) + 1]);
975 return stralloc("[ERROR: RRD::PRINT argument error]");
989 return stralloc("[ERROR: allocating strftime buffer]");
991 last = rrd_last(argc + 1, (char **) args - 1);
992 if (rrd_test_error()) {
994 malloc((strlen(rrd_get_error()) +
995 DS_NAM_SIZE) * sizeof(char));
996 sprintf(err, "[ERROR: %s]", rrd_get_error());
1000 tm_last = *localtime(&last);
1001 strftime(buf, 254, args[1], &tm_last);
1005 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
1007 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
1014 time_t now = time(NULL);
1021 return stralloc("[ERROR: allocating strftime buffer]");
1023 tm_now = *localtime(&now);
1024 strftime(buf, 254, args[0], &tm_now);
1028 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
1030 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
1033 /* Scan buffer until an unescaped '>' arives.
1034 * Update argument array with arguments found.
1035 * Return end cursor where parsing stopped, or NULL in case of failure.
1038 * To allow nested constructs, we call rrd_expand_vars() for arguments
1039 * that contain RRD::x directives. These introduce a small memory leak
1040 * since we have to stralloc the arguments the way parse() works.
1044 int *argument_count,
1047 char *getP; /* read cursor */
1048 char *putP; /* write cursor */
1049 char Quote; /* type of quote if in quoted string, 0 otherwise */
1050 int tagcount; /* open tag count */
1051 int in_arg; /* if we currently are parsing an argument or not */
1052 int argsz; /* argument array size */
1053 int curarg_contains_rrd_directives;
1055 /* local array of arguments while parsing */
1060 printf("<-- scanargs(%s) -->\n", line);
1064 *argument_count = 0;
1066 /* create initial argument array of char pointers */
1068 argv = (char **) malloc(argsz * sizeof(char *));
1074 /* skip leading blanks */
1075 while (isspace((int) *line)) {
1086 curarg_contains_rrd_directives = 0;
1088 /* start parsing 'line' for arguments */
1090 unsigned char c = *getP++;
1092 if (c == '>' && !Quote && !tagcount) {
1093 /* this is our closing tag, quit scanning */
1097 /* remove all special chars */
1104 if (Quote || tagcount) {
1105 /* copy quoted/tagged (=RRD expanded) string */
1107 } else if (in_arg) {
1108 /* end argument string */
1111 if (curarg_contains_rrd_directives) {
1113 rrd_expand_vars(stralloc(argv[argc - 1]));
1114 curarg_contains_rrd_directives = 0;
1119 case '"': /* Fall through */
1125 /* copy quoted string */
1130 /* reference start of argument string in argument array */
1131 argv[argc++] = putP;
1140 /* start new argument */
1141 argv[argc++] = putP;
1151 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1152 curarg_contains_rrd_directives = 1;
1159 /* check if our argument array is still large enough */
1160 if (argc == argsz) {
1161 /* resize argument array */
1163 argv = rrd_realloc(argv, argsz * sizeof(char *));
1164 if (*argv == NULL) {
1170 /* terminate last argument found */
1172 if (curarg_contains_rrd_directives) {
1173 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1179 printf("<-- arguments found [%d]\n", argc);
1180 for (n = 0; n < argc; n++) {
1181 printf("arg %02d: '%s'\n", n, argv[n]);
1185 printf("<!-- No arguments found -->\n");
1189 /* update caller's notion of the argument array and it's size */
1191 /* note this is a bit of a hack since the rrd_cgi code used to just put
1192 its arguments into a normal array starting at 0 ... since the rrd_*
1193 commands expect and argc/argv array we used to just shift everything
1194 by -1 ... this in turn exploded when a rrd_* function tried to print
1195 argv[0] ... hence we are now doing everything in argv style but hand
1196 over seemingly the old array ... but doing argv-1 will actually end
1197 up in a 'good' place now. */
1199 *arguments = argv+1;
1200 *argument_count = argc-1;
1206 /* Return new scanning cursor:
1207 pointer to char after closing bracket */
1213 * Parse(): scan current portion of buffer for given tag.
1214 * If found, parse tag arguments and call 'func' for it.
1215 * The result of func is inserted at the current position
1219 char **buf, /* buffer */
1220 long i, /* offset in buffer */
1221 char *tag, /* tag to handle */
1222 char * (*func) (long,
1223 const char **) /* function to call for 'tag' */
1226 /* the name of the vairable ... */
1233 size_t taglen = strlen(tag);
1235 /* Current position in buffer should start with 'tag' */
1236 if (strncmp((*buf) + i, tag, taglen) != 0) {
1239 /* .. and match exactly (a whitespace following 'tag') */
1240 if (!isspace(*((*buf) + i + taglen))) {
1244 printf("parse(): handling tag '%s'\n", tag);
1247 /* Scan for arguments following the tag;
1248 scanargs() puts \0 into *buf ... so after scanargs it is probably
1249 not a good time to use strlen on buf */
1250 end = scanargs((*buf) + i + taglen, &argc, &args);
1252 /* got arguments, call function for 'tag' with arguments */
1253 val = func(argc, (const char **) args);
1256 /* unable to parse arguments, undo 0-termination by scanargs */
1257 for (; argc > 0; argc--) {
1258 *((args[argc - 1]) - 1) = ' ';
1261 /* next call, try parsing at current offset +1 */
1262 end = (*buf) + i + 1;
1264 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1265 " Check original file. This may have been altered "
1266 "by parsing.]\n\n");
1269 /* remember offset where we have to continue parsing */
1270 end_offset = end - (*buf);
1274 valln = strlen(val);
1277 /* Optionally resize buffer to hold the replacement value:
1278 Calculating the new length of the buffer is simple. add current
1279 buffer pos (i) to length of string after replaced tag to length
1280 of replacement string and add 1 for the final zero ... */
1281 if (end - (*buf) < (i + valln)) {
1282 /* make sure we do not shrink the mallocd block */
1283 size_t newbufsize = i + strlen(end) + valln + 1;
1285 *buf = rrd_realloc(*buf, newbufsize);
1288 perror("Realoc buf:");
1293 /* Update new end pointer:
1294 make sure the 'end' pointer gets moved along with the
1295 buf pointer when realloc moves memory ... */
1296 end = (*buf) + end_offset;
1298 /* splice the variable:
1299 step 1. Shift pending data to make room for 'val' */
1300 memmove((*buf) + i + valln, end, strlen(end) + 1);
1302 /* step 2. Insert val */
1304 memmove((*buf) + i, val, valln);
1307 return (valln > 0 ? valln - 1 : valln);
1314 static char buf[60];
1316 tmptime = gmtime(now);
1317 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1325 printf("Content-type: %s\n", rrdcgiType);
1327 printf("Content-type: text/html\n");
1328 if (rrdcgiHeaderString)
1329 printf("%s", rrdcgiHeaderString);
1338 rrdcgiDebugLevel = level;
1340 rrdcgiDebugLevel = 0;
1342 rrdcgiDebugStderr = 0;
1344 rrdcgiDebugStderr = 1;
1347 char *rrdcgiDecodeString(
1352 for (cp = text, xp = text; *cp; cp++) {
1354 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1355 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1356 if (islower(*(cp + 1)))
1357 *(cp + 1) = toupper(*(cp + 1));
1358 if (islower(*(cp + 2)))
1359 *(cp + 2) = toupper(*(cp + 2));
1362 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1364 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1372 memset(xp, 0, cp - xp);
1376 /* rrdcgiReadVariables()
1378 * Read from stdin if no string is provided via CGI. Variables that
1379 * doesn't have a value associated with it doesn't get stored.
1381 s_var **rrdcgiReadVariables(
1387 char *cp, *ip, *esp, *sptr;
1392 cp = getenv("REQUEST_METHOD");
1393 ip = getenv("CONTENT_LENGTH");
1395 if (cp && !strcmp(cp, "POST")) {
1398 if ((line = (char *) malloc(length + 2)) == NULL)
1400 if (fgets(line, length + 1, stdin) == NULL)
1404 } else if (cp && !strcmp(cp, "GET")) {
1405 esp = getenv("QUERY_STRING");
1406 if (esp && strlen(esp)) {
1407 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1409 sprintf(line, "%s", esp);
1414 printf("(offline mode: enter name=value pairs on standard input)\n");
1415 memset(tmp, 0, sizeof(tmp));
1416 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1418 if (tmp[strlen(tmp) - 1] == '\n')
1419 tmp[strlen(tmp) - 1] = '&';
1421 length += strlen(tmp);
1422 len = (length + 1) * sizeof(char);
1423 if ((line = (char *) realloc(line, len)) == NULL)
1427 length = strlen(tmp);
1428 len = (length + 1) * sizeof(char);
1429 if ((line = (char *) malloc(len)) == NULL)
1431 memset(line, 0, len);
1435 memset(tmp, 0, sizeof(tmp));
1439 if (line[strlen(line) - 1] == '&')
1440 line[strlen(line) - 1] = '\0';
1444 * From now on all cgi variables are stored in the variable line
1445 * and look like foo=bar&foobar=barfoo&foofoo=
1448 if (rrdcgiDebugLevel > 0) {
1449 if (rrdcgiDebugStderr)
1450 fprintf(stderr, "Received cgi input: %s\n", line);
1453 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1457 for (cp = line; *cp; cp++)
1462 for (numargs = 1, cp = line; *cp; cp++)
1467 if (rrdcgiDebugLevel > 0) {
1468 if (rrdcgiDebugStderr)
1469 fprintf(stderr, "%d cgi variables found.\n", numargs);
1471 printf("%d cgi variables found.<br>\n", numargs);
1474 len = (numargs + 1) * sizeof(s_var *);
1475 if ((result = (s_var **) malloc(len)) == NULL)
1477 memset(result, 0, len);
1482 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1485 ip = cp + strlen(cp);
1487 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1499 /* try to find out if there's already such a variable */
1500 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1501 || !(strlen(result[k]->name) ==
1502 (size_t) (esp - cp))); k++);
1504 if (k == i) { /* No such variable yet */
1505 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1507 if ((result[i]->name =
1508 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1510 memset(result[i]->name, 0, esp - cp + 1);
1511 strncpy(result[i]->name, cp, esp - cp);
1513 if ((result[i]->value =
1514 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1516 memset(result[i]->value, 0, ip - esp + 1);
1517 strncpy(result[i]->value, cp, ip - esp);
1518 result[i]->value = rrdcgiDecodeString(result[i]->value);
1519 if (rrdcgiDebugLevel) {
1520 if (rrdcgiDebugStderr)
1521 fprintf(stderr, "%s: %s\n", result[i]->name,
1524 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1525 result[i]->name, result[i]->value);
1528 } else { /* There is already such a name, suppose a mutiple field */
1531 (strlen(result[k]->value) + (ip - esp) +
1533 if ((sptr = (char *) malloc(len)) == NULL)
1535 memset(sptr, 0, len);
1536 sprintf(sptr, "%s\n", result[k]->value);
1537 strncat(sptr, cp, ip - esp);
1538 free(result[k]->value);
1539 result[k]->value = rrdcgiDecodeString(sptr);
1549 * Read from stdin if no string is provided via CGI. Variables that
1550 * doesn't have a value associated with it doesn't get stored.
1558 vars = rrdcgiReadVariables();
1563 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1570 char *rrdcgiGetValue(
1576 if (!parms || !parms->vars)
1578 for (i = 0; parms->vars[i]; i++)
1579 if (!strcmp(name, parms->vars[i]->name)) {
1580 if (rrdcgiDebugLevel > 0) {
1581 if (rrdcgiDebugStderr)
1582 fprintf(stderr, "%s found as %s\n", name,
1583 parms->vars[i]->value);
1585 printf("%s found as %s<br>\n", name,
1586 parms->vars[i]->value);
1588 return parms->vars[i]->value;
1590 if (rrdcgiDebugLevel) {
1591 if (rrdcgiDebugStderr)
1592 fprintf(stderr, "%s not found\n", name);
1594 printf("%s not found<br>\n", name);
1599 void rrdcgiFreeList(
1604 for (i = 0; list[i] != NULL; i++)
1617 for (i = 0; parms->vars[i]; i++) {
1618 if (parms->vars[i]->name)
1619 free(parms->vars[i]->name);
1620 if (parms->vars[i]->value)
1621 free(parms->vars[i]->value);
1622 free(parms->vars[i]);
1628 if (rrdcgiHeaderString) {
1629 free(rrdcgiHeaderString);
1630 rrdcgiHeaderString = NULL;