1 /*****************************************************************************
2 * RRDtool 1.3.2 Copyright by Tobi Oetiker, 1997-2008
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++) {
380 /* create freeable version of the string */
389 nstr = malloc((strlen(str) + 1));
395 const char *file_name,
399 long writecnt = 0, totalcnt = MEMBLK;
404 if ((strcmp("-", file_name) == 0)) {
407 if ((input = fopen(file_name, "rb")) == NULL) {
408 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
416 } while (c != '\n' && !feof(input));
418 if (strcmp("-", file_name)) {
419 fseek(input, 0, SEEK_END);
420 /* have extra space for detecting EOF without realloc */
421 totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
422 if (totalcnt < MEMBLK)
423 totalcnt = MEMBLK; /* sanitize */
424 fseek(input, offset * sizeof(char), SEEK_SET);
426 if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
427 perror("Allocate Buffer:");
432 fread((*buffer) + writecnt, 1,
433 (totalcnt - writecnt) * sizeof(char), input);
434 if (writecnt >= totalcnt) {
437 rrd_realloc((*buffer),
438 (totalcnt + 4) * sizeof(char))) == NULL) {
439 perror("Realloc Buffer:");
443 } while (!feof(input));
444 (*buffer)[writecnt] = '\0';
445 if (strcmp("-", file_name) != 0) {
457 char *server_url = NULL;
460 struct option long_options[] = {
461 {"filter", no_argument, 0, 'f'},
465 #ifdef MUST_DISABLE_SIGFPE
466 signal(SIGFPE, SIG_IGN);
468 #ifdef MUST_DISABLE_FPMASK
472 opterr = 0; /* initialize getopt */
474 /* what do we get for cmdline arguments?
476 printf("%d-'%s'\n",i,argv[i]); */
478 int option_index = 0;
481 opt = getopt_long(argc, argv, "f", long_options, &option_index);
491 printf("unknown commandline option '%s'\n", argv[optind - 1]);
498 rrdcgiArg = rrdcgiInit();
499 server_url = getenv("SERVER_URL");
502 /* make sure we have one extra argument,
503 if there are others, we do not care Apache gives several */
505 /* if ( (optind != argc-2
506 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
507 && optind != argc-1) { */
509 if (optind >= argc) {
510 fprintf(stderr, "ERROR: expected a filename\n");
513 length = readfile(argv[optind], &buffer, 1);
516 if (rrd_test_error()) {
517 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
521 /* initialize variable heap */
525 /* some fake header for testing */
526 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
530 /* expand rrd directives in buffer recursivly */
531 for (i = 0; buffer[i]; i++) {
532 if (buffer[i] != '<')
535 parse(&buffer, i, "<RRD::CV", cgiget);
536 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
537 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
538 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
540 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
541 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
542 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
543 parse(&buffer, i, "<RRD::INCLUDE", includefile);
544 parse(&buffer, i, "<RRD::PRINT", drawprint);
545 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
546 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
547 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
548 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
549 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
550 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
551 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
555 printf("Content-Type: text/html\n"
556 "Content-Length: %zd\n", strlen(buffer));
558 if (labs(goodfor) > 0) {
562 printf("Last-Modified: %s\n", http_time(&now));
563 now += labs(goodfor);
564 printf("Expires: %s\n", http_time(&now));
566 printf("Refresh: %ld\n", labs(goodfor));
573 printf("%s", buffer);
584 /* remove occurrences of .. this is a general measure to make
585 paths which came in via cgi do not go UP ... */
592 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
595 return stralloc("[ERROR: allocating setenv buffer]");
597 sprintf(xyz, "%s=%s", args[0], args[1]);
598 if (putenv(xyz) == -1) {
600 return stralloc("[ERROR: failed to do putenv]");
604 return stralloc("[ERROR: setenv failed because not enough "
605 "arguments were defined]");
608 /* rrd interface to the variable function putvar() */
614 const char *result = putvar(args[0], args[1], 0 /* not const */ );
617 /* setvar does not return the value set */
620 return stralloc("[ERROR: putvar failed]");
622 return stralloc("[ERROR: putvar failed because not enough arguments "
626 /* rrd interface to the variable function putvar() */
627 char *rrdsetvarconst(
632 const char *result = putvar(args[0], args[1], 1 /* const */ );
635 /* setvar does not return the value set */
638 return stralloc("[ERROR: putvar failed]");
640 return stralloc("[ERROR: putvar failed because not enough arguments "
652 return stralloc("[ERROR: getenv failed because it did not "
653 "get 1 argument only]");
655 envvar = getenv(args[0]);
657 return stralloc(envvar);
659 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
660 return stralloc(buf);
672 return stralloc("[ERROR: getvar failed because it did not "
673 "get 1 argument only]");
675 value = getvar(args[0]);
677 return stralloc(value);
679 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
680 return stralloc(buf);
689 goodfor = atol(args[0]);
691 return stralloc("[ERROR: goodfor expected 1 argument]");
695 return stralloc("[ERROR: goodfor value must not be 0]");
701 char *rrdgetinternal(
706 if (strcasecmp(args[0], "VERSION") == 0) {
707 return stralloc(PACKAGE_VERSION);
708 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
709 return stralloc(__DATE__ " " __TIME__);
711 return stralloc("[ERROR: internal unknown argument]");
714 return stralloc("[ERROR: internal expected 1 argument]");
718 /* Format start or end times using strftime. We always need both the
719 * start and end times, because, either might be relative to the other.
721 #define MAX_STRFTIME_SIZE 256
726 rrd_time_value_t start_tv, end_tv;
727 char *parsetime_error = NULL;
728 char formatted[MAX_STRFTIME_SIZE];
730 time_t start_tmp, end_tmp;
732 /* Make sure that we were given the right number of args */
734 rrd_set_error("wrong number of args %d", argc);
738 /* Init start and end time */
739 rrd_parsetime("end-24h", &start_tv);
740 rrd_parsetime("now", &end_tv);
742 /* Parse the start and end times we were given */
743 if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
744 rrd_set_error("start time: %s", parsetime_error);
747 if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
748 rrd_set_error("end time: %s", parsetime_error);
751 if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
755 /* Do we do the start or end */
756 if (strcasecmp(args[0], "START") == 0) {
757 the_tm = localtime(&start_tmp);
758 } else if (strcasecmp(args[0], "END") == 0) {
759 the_tm = localtime(&end_tmp);
761 rrd_set_error("start/end not found in '%s'", args[0]);
766 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
767 return (stralloc(formatted));
769 rrd_set_error("strftime failed");
781 const char *filename = args[0];
783 readfile(filename, &buffer, 0);
784 if (rrd_test_error()) {
785 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
787 sprintf(err, "[ERROR: %s]", rrd_get_error());
794 return stralloc("[ERROR: No Inclue file defined]");
798 /* make a copy of buf and replace open/close brackets with '_' */
807 /* make a copy of the buffer */
815 if (*p == '<' || *p == '>') {
828 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
836 for (c = buf; *c != '\0'; c++)
839 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
840 perror("Malloc Buffer");
861 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
864 /* remove occurrences of .. this is a general measure to make
865 paths which came in via cgi do not go UP ... */
877 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
880 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
885 buf2 = malloc(strlen(buf) + 1);
887 perror("cgigetqp(): Malloc Path Buffer");
895 /* prevent mallicious paths from entering the system */
896 if (p[0] == '.' && p[1] == '.') {
908 /* Make sure the path is relative, e.g. does not start with '/' */
923 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
925 return stralloc("[ERROR: not enough arguments for RRD::CV]");
937 for (i = 0; i < argc; i++)
938 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
941 args[argc++] = "--imginfo";
942 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
946 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
948 return stralloc(calcpr[0]);
950 if (rrd_test_error()) {
952 malloc((strlen(rrd_get_error()) +
953 DS_NAM_SIZE) * sizeof(char));
954 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 *));
1073 /* skip leading blanks */
1074 while (isspace((int) *line)) {
1085 curarg_contains_rrd_directives = 0;
1087 /* start parsing 'line' for arguments */
1089 unsigned char c = *getP++;
1091 if (c == '>' && !Quote && !tagcount) {
1092 /* this is our closing tag, quit scanning */
1096 /* remove all special chars */
1103 if (Quote || tagcount) {
1104 /* copy quoted/tagged (=RRD expanded) string */
1106 } else if (in_arg) {
1107 /* end argument string */
1110 if (curarg_contains_rrd_directives) {
1112 rrd_expand_vars(stralloc(argv[argc - 1]));
1113 curarg_contains_rrd_directives = 0;
1118 case '"': /* Fall through */
1124 /* copy quoted string */
1129 /* reference start of argument string in argument array */
1130 argv[argc++] = putP;
1139 /* start new argument */
1140 argv[argc++] = putP;
1150 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1151 curarg_contains_rrd_directives = 1;
1158 /* check if our argument array is still large enough */
1159 if (argc == argsz) {
1160 /* resize argument array */
1162 argv = rrd_realloc(argv, argsz * sizeof(char *));
1163 if (*argv == NULL) {
1169 /* terminate last argument found */
1171 if (curarg_contains_rrd_directives) {
1172 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1178 printf("<-- arguments found [%d]\n", argc);
1179 for (n = 0; n < argc; n++) {
1180 printf("arg %02d: '%s'\n", n, argv[n]);
1184 printf("<!-- No arguments found -->\n");
1188 /* update caller's notion of the argument array and it's size */
1190 *argument_count = argc;
1196 /* Return new scanning cursor:
1197 pointer to char after closing bracket */
1203 * Parse(): scan current portion of buffer for given tag.
1204 * If found, parse tag arguments and call 'func' for it.
1205 * The result of func is inserted at the current position
1209 char **buf, /* buffer */
1210 long i, /* offset in buffer */
1211 char *tag, /* tag to handle */
1212 char * (*func) (long,
1213 const char **) /* function to call for 'tag' */
1216 /* the name of the vairable ... */
1223 size_t taglen = strlen(tag);
1225 /* Current position in buffer should start with 'tag' */
1226 if (strncmp((*buf) + i, tag, taglen) != 0) {
1229 /* .. and match exactly (a whitespace following 'tag') */
1230 if (!isspace(*((*buf) + i + taglen))) {
1234 printf("parse(): handling tag '%s'\n", tag);
1237 /* Scan for arguments following the tag;
1238 scanargs() puts \0 into *buf ... so after scanargs it is probably
1239 not a good time to use strlen on buf */
1240 end = scanargs((*buf) + i + taglen, &argc, &args);
1242 /* got arguments, call function for 'tag' with arguments */
1243 val = func(argc, (const char **) args);
1246 /* unable to parse arguments, undo 0-termination by scanargs */
1247 for (; argc > 0; argc--) {
1248 *((args[argc - 1]) - 1) = ' ';
1251 /* next call, try parsing at current offset +1 */
1252 end = (*buf) + i + 1;
1254 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1255 " Check original file. This may have been altered "
1256 "by parsing.]\n\n");
1259 /* remember offset where we have to continue parsing */
1260 end_offset = end - (*buf);
1264 valln = strlen(val);
1267 /* Optionally resize buffer to hold the replacement value:
1268 Calculating the new length of the buffer is simple. add current
1269 buffer pos (i) to length of string after replaced tag to length
1270 of replacement string and add 1 for the final zero ... */
1271 if (end - (*buf) < (i + valln)) {
1272 /* make sure we do not shrink the mallocd block */
1273 size_t newbufsize = i + strlen(end) + valln + 1;
1275 *buf = rrd_realloc(*buf, newbufsize);
1278 perror("Realoc buf:");
1283 /* Update new end pointer:
1284 make sure the 'end' pointer gets moved along with the
1285 buf pointer when realloc moves memory ... */
1286 end = (*buf) + end_offset;
1288 /* splice the variable:
1289 step 1. Shift pending data to make room for 'val' */
1290 memmove((*buf) + i + valln, end, strlen(end) + 1);
1292 /* step 2. Insert val */
1294 memmove((*buf) + i, val, valln);
1297 return (valln > 0 ? valln - 1 : valln);
1304 static char buf[60];
1306 tmptime = gmtime(now);
1307 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1315 printf("Content-type: %s\n", rrdcgiType);
1317 printf("Content-type: text/html\n");
1318 if (rrdcgiHeaderString)
1319 printf("%s", rrdcgiHeaderString);
1328 rrdcgiDebugLevel = level;
1330 rrdcgiDebugLevel = 0;
1332 rrdcgiDebugStderr = 0;
1334 rrdcgiDebugStderr = 1;
1337 char *rrdcgiDecodeString(
1342 for (cp = text, xp = text; *cp; cp++) {
1344 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1345 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1346 if (islower(*(cp + 1)))
1347 *(cp + 1) = toupper(*(cp + 1));
1348 if (islower(*(cp + 2)))
1349 *(cp + 2) = toupper(*(cp + 2));
1352 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1354 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1362 memset(xp, 0, cp - xp);
1366 /* rrdcgiReadVariables()
1368 * Read from stdin if no string is provided via CGI. Variables that
1369 * doesn't have a value associated with it doesn't get stored.
1371 s_var **rrdcgiReadVariables(
1377 char *cp, *ip, *esp, *sptr;
1382 cp = getenv("REQUEST_METHOD");
1383 ip = getenv("CONTENT_LENGTH");
1385 if (cp && !strcmp(cp, "POST")) {
1388 if ((line = (char *) malloc(length + 2)) == NULL)
1390 fgets(line, length + 1, stdin);
1393 } else if (cp && !strcmp(cp, "GET")) {
1394 esp = getenv("QUERY_STRING");
1395 if (esp && strlen(esp)) {
1396 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1398 sprintf(line, "%s", esp);
1403 printf("(offline mode: enter name=value pairs on standard input)\n");
1404 memset(tmp, 0, sizeof(tmp));
1405 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1407 if (tmp[strlen(tmp) - 1] == '\n')
1408 tmp[strlen(tmp) - 1] = '&';
1410 length += strlen(tmp);
1411 len = (length + 1) * sizeof(char);
1412 if ((line = (char *) realloc(line, len)) == NULL)
1416 length = strlen(tmp);
1417 len = (length + 1) * sizeof(char);
1418 if ((line = (char *) malloc(len)) == NULL)
1420 memset(line, 0, len);
1424 memset(tmp, 0, sizeof(tmp));
1428 if (line[strlen(line) - 1] == '&')
1429 line[strlen(line) - 1] = '\0';
1433 * From now on all cgi variables are stored in the variable line
1434 * and look like foo=bar&foobar=barfoo&foofoo=
1437 if (rrdcgiDebugLevel > 0) {
1438 if (rrdcgiDebugStderr)
1439 fprintf(stderr, "Received cgi input: %s\n", line);
1442 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1446 for (cp = line; *cp; cp++)
1451 for (numargs = 1, cp = line; *cp; cp++)
1456 if (rrdcgiDebugLevel > 0) {
1457 if (rrdcgiDebugStderr)
1458 fprintf(stderr, "%d cgi variables found.\n", numargs);
1460 printf("%d cgi variables found.<br>\n", numargs);
1463 len = (numargs + 1) * sizeof(s_var *);
1464 if ((result = (s_var **) malloc(len)) == NULL)
1466 memset(result, 0, len);
1471 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1474 ip = cp + strlen(cp);
1476 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1488 /* try to find out if there's already such a variable */
1489 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1490 || !(strlen(result[k]->name) ==
1491 (size_t) (esp - cp))); k++);
1493 if (k == i) { /* No such variable yet */
1494 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1496 if ((result[i]->name =
1497 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1499 memset(result[i]->name, 0, esp - cp + 1);
1500 strncpy(result[i]->name, cp, esp - cp);
1502 if ((result[i]->value =
1503 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1505 memset(result[i]->value, 0, ip - esp + 1);
1506 strncpy(result[i]->value, cp, ip - esp);
1507 result[i]->value = rrdcgiDecodeString(result[i]->value);
1508 if (rrdcgiDebugLevel) {
1509 if (rrdcgiDebugStderr)
1510 fprintf(stderr, "%s: %s\n", result[i]->name,
1513 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1514 result[i]->name, result[i]->value);
1517 } else { /* There is already such a name, suppose a mutiple field */
1520 (strlen(result[k]->value) + (ip - esp) +
1522 if ((sptr = (char *) malloc(len)) == NULL)
1524 memset(sptr, 0, len);
1525 sprintf(sptr, "%s\n", result[k]->value);
1526 strncat(sptr, cp, ip - esp);
1527 free(result[k]->value);
1528 result[k]->value = rrdcgiDecodeString(sptr);
1538 * Read from stdin if no string is provided via CGI. Variables that
1539 * doesn't have a value associated with it doesn't get stored.
1547 vars = rrdcgiReadVariables();
1552 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1559 char *rrdcgiGetValue(
1565 if (!parms || !parms->vars)
1567 for (i = 0; parms->vars[i]; i++)
1568 if (!strcmp(name, parms->vars[i]->name)) {
1569 if (rrdcgiDebugLevel > 0) {
1570 if (rrdcgiDebugStderr)
1571 fprintf(stderr, "%s found as %s\n", name,
1572 parms->vars[i]->value);
1574 printf("%s found as %s<br>\n", name,
1575 parms->vars[i]->value);
1577 return parms->vars[i]->value;
1579 if (rrdcgiDebugLevel) {
1580 if (rrdcgiDebugStderr)
1581 fprintf(stderr, "%s not found\n", name);
1583 printf("%s not found<br>\n", name);
1588 void rrdcgiFreeList(
1593 for (i = 0; list[i] != NULL; i++)
1606 for (i = 0; parms->vars[i]; i++) {
1607 if (parms->vars[i]->name)
1608 free(parms->vars[i]->name);
1609 if (parms->vars[i]->value)
1610 free(parms->vars[i]->value);
1611 free(parms->vars[i]);
1617 if (rrdcgiHeaderString) {
1618 free(rrdcgiHeaderString);
1619 rrdcgiHeaderString = NULL;