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 */
397 const char *file_name,
401 long writecnt = 0, totalcnt = MEMBLK;
406 if ((strcmp("-", file_name) == 0)) {
409 if ((input = fopen(file_name, "rb")) == NULL) {
410 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
418 } while (c != '\n' && !feof(input));
420 if (strcmp("-", file_name)) {
421 fseek(input, 0, SEEK_END);
422 /* have extra space for detecting EOF without realloc */
423 totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
424 if (totalcnt < MEMBLK)
425 totalcnt = MEMBLK; /* sanitize */
426 fseek(input, offset * sizeof(char), SEEK_SET);
428 if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
429 perror("Allocate Buffer:");
434 fread((*buffer) + writecnt, 1,
435 (totalcnt - writecnt) * sizeof(char), input);
436 if (writecnt >= totalcnt) {
439 rrd_realloc((*buffer),
440 (totalcnt + 4) * sizeof(char))) == NULL) {
441 perror("Realloc Buffer:");
445 } while (!feof(input));
446 (*buffer)[writecnt] = '\0';
447 if (strcmp("-", file_name) != 0) {
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();
501 /* make sure we have one extra argument,
502 if there are others, we do not care Apache gives several */
504 /* if ( (optind != argc-2
505 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
506 && optind != argc-1) { */
508 if (optind >= argc) {
509 fprintf(stderr, "ERROR: expected a filename\n");
512 readfile(argv[optind], &buffer, 1);
515 if (rrd_test_error()) {
516 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
520 /* initialize variable heap */
524 /* some fake header for testing */
525 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
529 /* expand rrd directives in buffer recursivly */
530 for (i = 0; buffer[i]; i++) {
531 if (buffer[i] != '<')
534 parse(&buffer, i, "<RRD::CV", cgiget);
535 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
536 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
537 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
539 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
540 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
541 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
542 parse(&buffer, i, "<RRD::INCLUDE", includefile);
543 parse(&buffer, i, "<RRD::PRINT", drawprint);
544 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
545 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
546 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
547 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
548 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
549 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
550 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
554 printf("Content-Type: text/html\n"
555 "Content-Length: %zd\n", strlen(buffer));
557 if (labs(goodfor) > 0) {
561 printf("Last-Modified: %s\n", http_time(&now));
562 now += labs(goodfor);
563 printf("Expires: %s\n", http_time(&now));
565 printf("Refresh: %ld\n", labs(goodfor));
572 printf("%s", buffer);
583 /* remove occurrences of .. this is a general measure to make
584 paths which came in via cgi do not go UP ... */
591 const size_t len = strlen(args[0]) + strlen(args[1]) + 2;
592 char *xyz = malloc(len);
595 return stralloc("[ERROR: allocating setenv buffer]");
597 snprintf(xyz, len, "%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 const size_t len = strlen(rrd_get_error()) + DS_NAM_SIZE;
786 char *err = malloc(len);
788 snprintf(err, len, "[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()) {
952 const size_t len = strlen(rrd_get_error()) + DS_NAM_SIZE;
953 char *err = malloc(len);
954 snprintf(err, len, "[ERROR: %s]", rrd_get_error());
966 if (argc == 1 && calcpr) {
969 while (calcpr[i] != NULL)
970 i++; /*determine number lines in calcpr */
971 if (atol(args[0]) < i - 1)
972 return stralloc(calcpr[atol(args[0]) + 1]);
974 return stralloc("[ERROR: RRD::PRINT argument error]");
988 return stralloc("[ERROR: allocating strftime buffer]");
990 /* not raising argc in step with args - 1 since the last argument
991 will be used below for strftime */
993 last = rrd_last(argc, (char **) args - 1);
994 if (rrd_test_error()) {
995 const size_t len = strlen(rrd_get_error()) + DS_NAM_SIZE;
996 char *err = malloc(len);
997 snprintf(err, len, "[ERROR: %s]", rrd_get_error());
1001 tm_last = *localtime(&last);
1002 strftime(buf, 254, args[1], &tm_last);
1005 return stralloc("[ERROR: expected <RRD::TIME::LAST file.rrd strftime-format>]");
1012 time_t now = time(NULL);
1019 return stralloc("[ERROR: allocating strftime buffer]");
1021 tm_now = *localtime(&now);
1022 strftime(buf, 254, args[0], &tm_now);
1026 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
1028 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
1031 /* Scan buffer until an unescaped '>' arives.
1032 * Update argument array with arguments found.
1033 * Return end cursor where parsing stopped, or NULL in case of failure.
1036 * To allow nested constructs, we call rrd_expand_vars() for arguments
1037 * that contain RRD::x directives. These introduce a small memory leak
1038 * since we have to stralloc the arguments the way parse() works.
1042 int *argument_count,
1045 char *getP; /* read cursor */
1046 char *putP; /* write cursor */
1047 char Quote; /* type of quote if in quoted string, 0 otherwise */
1048 int tagcount; /* open tag count */
1049 int in_arg; /* if we currently are parsing an argument or not */
1050 int argsz; /* argument array size */
1051 int curarg_contains_rrd_directives;
1053 /* local array of arguments while parsing */
1058 printf("<-- scanargs(%s) -->\n", line);
1062 *argument_count = 0;
1064 /* create initial argument array of char pointers */
1066 argv = (char **) malloc(argsz * sizeof(char *));
1072 /* skip leading blanks */
1073 while (isspace((int) *line)) {
1084 curarg_contains_rrd_directives = 0;
1086 /* start parsing 'line' for arguments */
1088 unsigned char c = *getP++;
1090 if (c == '>' && !Quote && !tagcount) {
1091 /* this is our closing tag, quit scanning */
1095 /* remove all special chars */
1102 if (Quote || tagcount) {
1103 /* copy quoted/tagged (=RRD expanded) string */
1105 } else if (in_arg) {
1106 /* end argument string */
1109 if (curarg_contains_rrd_directives) {
1111 rrd_expand_vars(stralloc(argv[argc - 1]));
1112 curarg_contains_rrd_directives = 0;
1117 case '"': /* Fall through */
1123 /* copy quoted string */
1128 /* reference start of argument string in argument array */
1129 argv[argc++] = putP;
1138 /* start new argument */
1139 argv[argc++] = putP;
1149 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1150 curarg_contains_rrd_directives = 1;
1157 /* check if our argument array is still large enough */
1158 if (argc == argsz) {
1159 /* resize argument array */
1161 argv = rrd_realloc(argv, argsz * sizeof(char *));
1162 if (*argv == NULL) {
1168 /* terminate last argument found */
1170 if (curarg_contains_rrd_directives) {
1171 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1177 printf("<-- arguments found [%d]\n", argc);
1178 for (n = 0; n < argc; n++) {
1179 printf("arg %02d: '%s'\n", n, argv[n]);
1183 printf("<!-- No arguments found -->\n");
1187 /* update caller's notion of the argument array and it's size */
1189 /* note this is a bit of a hack since the rrd_cgi code used to just put
1190 its arguments into a normal array starting at 0 ... since the rrd_*
1191 commands expect and argc/argv array we used to just shift everything
1192 by -1 ... this in turn exploded when a rrd_* function tried to print
1193 argv[0] ... hence we are now doing everything in argv style but hand
1194 over seemingly the old array ... but doing argv-1 will actually end
1195 up in a 'good' place now. */
1197 *arguments = argv+1;
1198 *argument_count = argc-1;
1204 /* Return new scanning cursor:
1205 pointer to char after closing bracket */
1211 * Parse(): scan current portion of buffer for given tag.
1212 * If found, parse tag arguments and call 'func' for it.
1213 * The result of func is inserted at the current position
1217 char **buf, /* buffer */
1218 long i, /* offset in buffer */
1219 char *tag, /* tag to handle */
1220 char * (*func) (long,
1221 const char **) /* function to call for 'tag' */
1224 /* the name of the vairable ... */
1231 size_t taglen = strlen(tag);
1233 /* Current position in buffer should start with 'tag' */
1234 if (strncmp((*buf) + i, tag, taglen) != 0) {
1237 /* .. and match exactly (a whitespace following 'tag') */
1238 if (!isspace(*((*buf) + i + taglen))) {
1242 printf("parse(): handling tag '%s'\n", tag);
1245 /* Scan for arguments following the tag;
1246 scanargs() puts \0 into *buf ... so after scanargs it is probably
1247 not a good time to use strlen on buf */
1248 end = scanargs((*buf) + i + taglen, &argc, &args);
1250 /* got arguments, call function for 'tag' with arguments */
1251 val = func(argc, (const char **) args);
1254 /* next call, try parsing at current offset +1 */
1255 end = (*buf) + i + 1;
1257 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1258 " Check original file. This may have been altered "
1259 "by parsing.]\n\n");
1262 /* remember offset where we have to continue parsing */
1263 end_offset = end - (*buf);
1267 valln = strlen(val);
1270 /* Optionally resize buffer to hold the replacement value:
1271 Calculating the new length of the buffer is simple. add current
1272 buffer pos (i) to length of string after replaced tag to length
1273 of replacement string and add 1 for the final zero ... */
1274 if (end - (*buf) < (i + valln)) {
1275 /* make sure we do not shrink the mallocd block */
1276 size_t newbufsize = i + strlen(end) + valln + 1;
1278 *buf = rrd_realloc(*buf, newbufsize);
1281 perror("Realoc buf:");
1286 /* Update new end pointer:
1287 make sure the 'end' pointer gets moved along with the
1288 buf pointer when realloc moves memory ... */
1289 end = (*buf) + end_offset;
1291 /* splice the variable:
1292 step 1. Shift pending data to make room for 'val' */
1293 memmove((*buf) + i + valln, end, strlen(end) + 1);
1295 /* step 2. Insert val */
1297 memmove((*buf) + i, val, valln);
1300 return (valln > 0 ? valln - 1 : valln);
1307 static char buf[60];
1309 tmptime = gmtime(now);
1310 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1318 printf("Content-type: %s\n", rrdcgiType);
1320 printf("Content-type: text/html\n");
1321 if (rrdcgiHeaderString)
1322 printf("%s", rrdcgiHeaderString);
1331 rrdcgiDebugLevel = level;
1333 rrdcgiDebugLevel = 0;
1335 rrdcgiDebugStderr = 0;
1337 rrdcgiDebugStderr = 1;
1340 char *rrdcgiDecodeString(
1345 for (cp = text, xp = text; *cp; cp++) {
1347 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1348 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1349 if (islower(*(cp + 1)))
1350 *(cp + 1) = toupper(*(cp + 1));
1351 if (islower(*(cp + 2)))
1352 *(cp + 2) = toupper(*(cp + 2));
1355 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1357 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1365 memset(xp, 0, cp - xp);
1369 /* rrdcgiReadVariables()
1371 * Read from stdin if no string is provided via CGI. Variables that
1372 * doesn't have a value associated with it doesn't get stored.
1374 s_var **rrdcgiReadVariables(
1380 char *cp, *ip, *esp, *sptr;
1386 cp = getenv("REQUEST_METHOD");
1387 ip = getenv("CONTENT_LENGTH");
1389 if (cp && !strcmp(cp, "POST")) {
1392 if ((line = (char *) malloc(length + 2)) == NULL)
1394 if (fgets(line, length + 1, stdin) == NULL)
1398 } else if (cp && !strcmp(cp, "GET")) {
1399 esp = getenv("QUERY_STRING");
1400 if (esp && strlen(esp)) {
1401 if ((line = strdup(esp)) == NULL)
1407 printf("(offline mode: enter name=value pairs on standard input)\n");
1408 memset(tmp, 0, sizeof(tmp));
1409 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1410 if ((tmplen = strlen(tmp)) != 0) {
1411 if (tmp[tmplen - 1] == '\n')
1412 tmp[tmplen - 1] = '&';
1414 len = (length + 1) * sizeof(char);
1415 if ((unsigned) length > tmplen) {
1416 if ((line = (char *) realloc(line, len)) == NULL)
1418 strncat(line, tmp, tmplen);
1420 if ((line = strdup(tmp)) == NULL)
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 */
1519 len = strlen(result[k]->value) + (ip - esp) + 2;
1520 if ((sptr = (char *) calloc(len, sizeof(char))) == NULL)
1522 snprintf(sptr, len, "%s\n%s", result[k]->value, cp);
1523 free(result[k]->value);
1524 result[k]->value = rrdcgiDecodeString(sptr);
1534 * Read from stdin if no string is provided via CGI. Variables that
1535 * doesn't have a value associated with it doesn't get stored.
1543 vars = rrdcgiReadVariables();
1548 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1555 char *rrdcgiGetValue(
1561 if (!parms || !parms->vars)
1563 for (i = 0; parms->vars[i]; i++)
1564 if (!strcmp(name, parms->vars[i]->name)) {
1565 if (rrdcgiDebugLevel > 0) {
1566 if (rrdcgiDebugStderr)
1567 fprintf(stderr, "%s found as %s\n", name,
1568 parms->vars[i]->value);
1570 printf("%s found as %s<br>\n", name,
1571 parms->vars[i]->value);
1573 return parms->vars[i]->value;
1575 if (rrdcgiDebugLevel) {
1576 if (rrdcgiDebugStderr)
1577 fprintf(stderr, "%s not found\n", name);
1579 printf("%s not found<br>\n", name);
1584 void rrdcgiFreeList(
1589 for (i = 0; list[i] != NULL; i++)
1602 for (i = 0; parms->vars[i]; i++) {
1603 if (parms->vars[i]->name)
1604 free(parms->vars[i]->name);
1605 if (parms->vars[i]->value)
1606 free(parms->vars[i]->value);
1607 free(parms->vars[i]);
1613 if (rrdcgiHeaderString) {
1614 free(rrdcgiHeaderString);
1615 rrdcgiHeaderString = NULL;