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) {
459 char *server_url = NULL;
462 struct option long_options[] = {
463 {"filter", no_argument, 0, 'f'},
467 #ifdef MUST_DISABLE_SIGFPE
468 signal(SIGFPE, SIG_IGN);
470 #ifdef MUST_DISABLE_FPMASK
474 opterr = 0; /* initialize getopt */
476 /* what do we get for cmdline arguments?
478 printf("%d-'%s'\n",i,argv[i]); */
480 int option_index = 0;
483 opt = getopt_long(argc, argv, "f", long_options, &option_index);
493 printf("unknown commandline option '%s'\n", argv[optind - 1]);
500 rrdcgiArg = rrdcgiInit();
501 server_url = getenv("SERVER_URL");
504 /* make sure we have one extra argument,
505 if there are others, we do not care Apache gives several */
507 /* if ( (optind != argc-2
508 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
509 && optind != argc-1) { */
511 if (optind >= argc) {
512 fprintf(stderr, "ERROR: expected a filename\n");
515 length = readfile(argv[optind], &buffer, 1);
518 if (rrd_test_error()) {
519 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
523 /* initialize variable heap */
527 /* some fake header for testing */
528 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
532 /* expand rrd directives in buffer recursivly */
533 for (i = 0; buffer[i]; i++) {
534 if (buffer[i] != '<')
537 parse(&buffer, i, "<RRD::CV", cgiget);
538 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
539 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
540 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
542 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
543 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
544 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
545 parse(&buffer, i, "<RRD::INCLUDE", includefile);
546 parse(&buffer, i, "<RRD::PRINT", drawprint);
547 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
548 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
549 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
550 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
551 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
552 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
553 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
557 printf("Content-Type: text/html\n"
558 "Content-Length: %zd\n", strlen(buffer));
560 if (labs(goodfor) > 0) {
564 printf("Last-Modified: %s\n", http_time(&now));
565 now += labs(goodfor);
566 printf("Expires: %s\n", http_time(&now));
568 printf("Refresh: %ld\n", labs(goodfor));
575 printf("%s", buffer);
586 /* remove occurrences of .. this is a general measure to make
587 paths which came in via cgi do not go UP ... */
594 const size_t len = strlen(args[0]) + strlen(args[1]) + 2;
595 char *xyz = malloc(len);
598 return stralloc("[ERROR: allocating setenv buffer]");
600 snprintf(xyz, len, "%s=%s", args[0], args[1]);
601 if (putenv(xyz) == -1) {
603 return stralloc("[ERROR: failed to do putenv]");
607 return stralloc("[ERROR: setenv failed because not enough "
608 "arguments were defined]");
611 /* rrd interface to the variable function putvar() */
617 const char *result = putvar(args[0], args[1], 0 /* not const */ );
620 /* setvar does not return the value set */
623 return stralloc("[ERROR: putvar failed]");
625 return stralloc("[ERROR: putvar failed because not enough arguments "
629 /* rrd interface to the variable function putvar() */
630 char *rrdsetvarconst(
635 const char *result = putvar(args[0], args[1], 1 /* const */ );
638 /* setvar does not return the value set */
641 return stralloc("[ERROR: putvar failed]");
643 return stralloc("[ERROR: putvar failed because not enough arguments "
655 return stralloc("[ERROR: getenv failed because it did not "
656 "get 1 argument only]");
658 envvar = getenv(args[0]);
660 return stralloc(envvar);
662 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
663 return stralloc(buf);
675 return stralloc("[ERROR: getvar failed because it did not "
676 "get 1 argument only]");
678 value = getvar(args[0]);
680 return stralloc(value);
682 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
683 return stralloc(buf);
692 goodfor = atol(args[0]);
694 return stralloc("[ERROR: goodfor expected 1 argument]");
698 return stralloc("[ERROR: goodfor value must not be 0]");
704 char *rrdgetinternal(
709 if (strcasecmp(args[0], "VERSION") == 0) {
710 return stralloc(PACKAGE_VERSION);
711 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
712 return stralloc(__DATE__ " " __TIME__);
714 return stralloc("[ERROR: internal unknown argument]");
717 return stralloc("[ERROR: internal expected 1 argument]");
721 /* Format start or end times using strftime. We always need both the
722 * start and end times, because, either might be relative to the other.
724 #define MAX_STRFTIME_SIZE 256
729 rrd_time_value_t start_tv, end_tv;
730 char *parsetime_error = NULL;
731 char formatted[MAX_STRFTIME_SIZE];
733 time_t start_tmp, end_tmp;
735 /* Make sure that we were given the right number of args */
737 rrd_set_error("wrong number of args %d", argc);
741 /* Init start and end time */
742 rrd_parsetime("end-24h", &start_tv);
743 rrd_parsetime("now", &end_tv);
745 /* Parse the start and end times we were given */
746 if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
747 rrd_set_error("start time: %s", parsetime_error);
750 if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
751 rrd_set_error("end time: %s", parsetime_error);
754 if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
758 /* Do we do the start or end */
759 if (strcasecmp(args[0], "START") == 0) {
760 the_tm = localtime(&start_tmp);
761 } else if (strcasecmp(args[0], "END") == 0) {
762 the_tm = localtime(&end_tmp);
764 rrd_set_error("start/end not found in '%s'", args[0]);
769 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
770 return (stralloc(formatted));
772 rrd_set_error("strftime failed");
784 const char *filename = args[0];
786 readfile(filename, &buffer, 0);
787 if (rrd_test_error()) {
788 const size_t len = strlen(rrd_get_error()) + DS_NAM_SIZE;
789 char *err = malloc(len);
791 snprintf(err, len, "[ERROR: %s]", rrd_get_error());
798 return stralloc("[ERROR: No Inclue file defined]");
802 /* make a copy of buf and replace open/close brackets with '_' */
811 /* make a copy of the buffer */
819 if (*p == '<' || *p == '>') {
832 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
840 for (c = buf; *c != '\0'; c++)
843 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
844 perror("Malloc Buffer");
865 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
868 /* remove occurrences of .. this is a general measure to make
869 paths which came in via cgi do not go UP ... */
881 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
884 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
889 buf2 = malloc(strlen(buf) + 1);
891 perror("cgigetqp(): Malloc Path Buffer");
899 /* prevent mallicious paths from entering the system */
900 if (p[0] == '.' && p[1] == '.') {
912 /* Make sure the path is relative, e.g. does not start with '/' */
927 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
929 return stralloc("[ERROR: not enough arguments for RRD::CV]");
941 for (i = 0; i < argc; i++)
942 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
945 args[argc++] = "--imginfo";
946 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
950 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
952 return stralloc(calcpr[0]);
954 if (rrd_test_error()) {
955 const size_t len = strlen(rrd_get_error()) + DS_NAM_SIZE;
956 char *err = malloc(len);
957 snprintf(err, len, "[ERROR: %s]", rrd_get_error());
969 if (argc == 1 && calcpr) {
972 while (calcpr[i] != NULL)
973 i++; /*determine number lines in calcpr */
974 if (atol(args[0]) < i - 1)
975 return stralloc(calcpr[atol(args[0]) + 1]);
977 return stralloc("[ERROR: RRD::PRINT argument error]");
991 return stralloc("[ERROR: allocating strftime buffer]");
993 /* not raising argc in step with args - 1 since the last argument
994 will be used below for strftime */
996 last = rrd_last(argc, (char **) args - 1);
997 if (rrd_test_error()) {
998 const size_t len = strlen(rrd_get_error()) + DS_NAM_SIZE;
999 char *err = malloc(len);
1000 snprintf(err, len, "[ERROR: %s]", rrd_get_error());
1004 tm_last = *localtime(&last);
1005 strftime(buf, 254, args[1], &tm_last);
1008 return stralloc("[ERROR: expected <RRD::TIME::LAST file.rrd strftime-format>]");
1015 time_t now = time(NULL);
1022 return stralloc("[ERROR: allocating strftime buffer]");
1024 tm_now = *localtime(&now);
1025 strftime(buf, 254, args[0], &tm_now);
1029 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
1031 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
1034 /* Scan buffer until an unescaped '>' arives.
1035 * Update argument array with arguments found.
1036 * Return end cursor where parsing stopped, or NULL in case of failure.
1039 * To allow nested constructs, we call rrd_expand_vars() for arguments
1040 * that contain RRD::x directives. These introduce a small memory leak
1041 * since we have to stralloc the arguments the way parse() works.
1045 int *argument_count,
1048 char *getP; /* read cursor */
1049 char *putP; /* write cursor */
1050 char Quote; /* type of quote if in quoted string, 0 otherwise */
1051 int tagcount; /* open tag count */
1052 int in_arg; /* if we currently are parsing an argument or not */
1053 int argsz; /* argument array size */
1054 int curarg_contains_rrd_directives;
1056 /* local array of arguments while parsing */
1061 printf("<-- scanargs(%s) -->\n", line);
1065 *argument_count = 0;
1067 /* create initial argument array of char pointers */
1069 argv = (char **) malloc(argsz * sizeof(char *));
1075 /* skip leading blanks */
1076 while (isspace((int) *line)) {
1087 curarg_contains_rrd_directives = 0;
1089 /* start parsing 'line' for arguments */
1091 unsigned char c = *getP++;
1093 if (c == '>' && !Quote && !tagcount) {
1094 /* this is our closing tag, quit scanning */
1098 /* remove all special chars */
1105 if (Quote || tagcount) {
1106 /* copy quoted/tagged (=RRD expanded) string */
1108 } else if (in_arg) {
1109 /* end argument string */
1112 if (curarg_contains_rrd_directives) {
1114 rrd_expand_vars(stralloc(argv[argc - 1]));
1115 curarg_contains_rrd_directives = 0;
1120 case '"': /* Fall through */
1126 /* copy quoted string */
1131 /* reference start of argument string in argument array */
1132 argv[argc++] = putP;
1141 /* start new argument */
1142 argv[argc++] = putP;
1152 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1153 curarg_contains_rrd_directives = 1;
1160 /* check if our argument array is still large enough */
1161 if (argc == argsz) {
1162 /* resize argument array */
1164 argv = rrd_realloc(argv, argsz * sizeof(char *));
1165 if (*argv == NULL) {
1171 /* terminate last argument found */
1173 if (curarg_contains_rrd_directives) {
1174 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1180 printf("<-- arguments found [%d]\n", argc);
1181 for (n = 0; n < argc; n++) {
1182 printf("arg %02d: '%s'\n", n, argv[n]);
1186 printf("<!-- No arguments found -->\n");
1190 /* update caller's notion of the argument array and it's size */
1192 /* note this is a bit of a hack since the rrd_cgi code used to just put
1193 its arguments into a normal array starting at 0 ... since the rrd_*
1194 commands expect and argc/argv array we used to just shift everything
1195 by -1 ... this in turn exploded when a rrd_* function tried to print
1196 argv[0] ... hence we are now doing everything in argv style but hand
1197 over seemingly the old array ... but doing argv-1 will actually end
1198 up in a 'good' place now. */
1200 *arguments = argv+1;
1201 *argument_count = argc-1;
1207 /* Return new scanning cursor:
1208 pointer to char after closing bracket */
1214 * Parse(): scan current portion of buffer for given tag.
1215 * If found, parse tag arguments and call 'func' for it.
1216 * The result of func is inserted at the current position
1220 char **buf, /* buffer */
1221 long i, /* offset in buffer */
1222 char *tag, /* tag to handle */
1223 char * (*func) (long,
1224 const char **) /* function to call for 'tag' */
1227 /* the name of the vairable ... */
1234 size_t taglen = strlen(tag);
1236 /* Current position in buffer should start with 'tag' */
1237 if (strncmp((*buf) + i, tag, taglen) != 0) {
1240 /* .. and match exactly (a whitespace following 'tag') */
1241 if (!isspace(*((*buf) + i + taglen))) {
1245 printf("parse(): handling tag '%s'\n", tag);
1248 /* Scan for arguments following the tag;
1249 scanargs() puts \0 into *buf ... so after scanargs it is probably
1250 not a good time to use strlen on buf */
1251 end = scanargs((*buf) + i + taglen, &argc, &args);
1253 /* got arguments, call function for 'tag' with arguments */
1254 val = func(argc, (const char **) args);
1257 /* next call, try parsing at current offset +1 */
1258 end = (*buf) + i + 1;
1260 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1261 " Check original file. This may have been altered "
1262 "by parsing.]\n\n");
1265 /* remember offset where we have to continue parsing */
1266 end_offset = end - (*buf);
1270 valln = strlen(val);
1273 /* Optionally resize buffer to hold the replacement value:
1274 Calculating the new length of the buffer is simple. add current
1275 buffer pos (i) to length of string after replaced tag to length
1276 of replacement string and add 1 for the final zero ... */
1277 if (end - (*buf) < (i + valln)) {
1278 /* make sure we do not shrink the mallocd block */
1279 size_t newbufsize = i + strlen(end) + valln + 1;
1281 *buf = rrd_realloc(*buf, newbufsize);
1284 perror("Realoc buf:");
1289 /* Update new end pointer:
1290 make sure the 'end' pointer gets moved along with the
1291 buf pointer when realloc moves memory ... */
1292 end = (*buf) + end_offset;
1294 /* splice the variable:
1295 step 1. Shift pending data to make room for 'val' */
1296 memmove((*buf) + i + valln, end, strlen(end) + 1);
1298 /* step 2. Insert val */
1300 memmove((*buf) + i, val, valln);
1303 return (valln > 0 ? valln - 1 : valln);
1310 static char buf[60];
1312 tmptime = gmtime(now);
1313 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1321 printf("Content-type: %s\n", rrdcgiType);
1323 printf("Content-type: text/html\n");
1324 if (rrdcgiHeaderString)
1325 printf("%s", rrdcgiHeaderString);
1334 rrdcgiDebugLevel = level;
1336 rrdcgiDebugLevel = 0;
1338 rrdcgiDebugStderr = 0;
1340 rrdcgiDebugStderr = 1;
1343 char *rrdcgiDecodeString(
1348 for (cp = text, xp = text; *cp; cp++) {
1350 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1351 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1352 if (islower(*(cp + 1)))
1353 *(cp + 1) = toupper(*(cp + 1));
1354 if (islower(*(cp + 2)))
1355 *(cp + 2) = toupper(*(cp + 2));
1358 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1360 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1368 memset(xp, 0, cp - xp);
1372 /* rrdcgiReadVariables()
1374 * Read from stdin if no string is provided via CGI. Variables that
1375 * doesn't have a value associated with it doesn't get stored.
1377 s_var **rrdcgiReadVariables(
1383 char *cp, *ip, *esp, *sptr;
1389 cp = getenv("REQUEST_METHOD");
1390 ip = getenv("CONTENT_LENGTH");
1392 if (cp && !strcmp(cp, "POST")) {
1395 if ((line = (char *) malloc(length + 2)) == NULL)
1397 if (fgets(line, length + 1, stdin) == NULL)
1401 } else if (cp && !strcmp(cp, "GET")) {
1402 esp = getenv("QUERY_STRING");
1403 if (esp && strlen(esp)) {
1404 if ((line = strdup(esp)) == NULL)
1410 printf("(offline mode: enter name=value pairs on standard input)\n");
1411 memset(tmp, 0, sizeof(tmp));
1412 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1413 if ((tmplen = strlen(tmp)) != 0) {
1414 if (tmp[tmplen - 1] == '\n')
1415 tmp[tmplen - 1] = '&';
1417 len = (length + 1) * sizeof(char);
1418 if ((unsigned) length > tmplen) {
1419 if ((line = (char *) realloc(line, len)) == NULL)
1421 strncat(line, tmp, tmplen);
1423 if ((line = strdup(tmp)) == NULL)
1427 memset(tmp, 0, sizeof(tmp));
1431 if (line[strlen(line) - 1] == '&')
1432 line[strlen(line) - 1] = '\0';
1436 * From now on all cgi variables are stored in the variable line
1437 * and look like foo=bar&foobar=barfoo&foofoo=
1440 if (rrdcgiDebugLevel > 0) {
1441 if (rrdcgiDebugStderr)
1442 fprintf(stderr, "Received cgi input: %s\n", line);
1445 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1449 for (cp = line; *cp; cp++)
1454 for (numargs = 1, cp = line; *cp; cp++)
1459 if (rrdcgiDebugLevel > 0) {
1460 if (rrdcgiDebugStderr)
1461 fprintf(stderr, "%d cgi variables found.\n", numargs);
1463 printf("%d cgi variables found.<br>\n", numargs);
1466 len = (numargs + 1) * sizeof(s_var *);
1467 if ((result = (s_var **) malloc(len)) == NULL)
1469 memset(result, 0, len);
1474 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1477 ip = cp + strlen(cp);
1479 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1491 /* try to find out if there's already such a variable */
1492 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1493 || !(strlen(result[k]->name) ==
1494 (size_t) (esp - cp))); k++);
1496 if (k == i) { /* No such variable yet */
1497 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1499 if ((result[i]->name =
1500 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1502 memset(result[i]->name, 0, esp - cp + 1);
1503 strncpy(result[i]->name, cp, esp - cp);
1505 if ((result[i]->value =
1506 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1508 memset(result[i]->value, 0, ip - esp + 1);
1509 strncpy(result[i]->value, cp, ip - esp);
1510 result[i]->value = rrdcgiDecodeString(result[i]->value);
1511 if (rrdcgiDebugLevel) {
1512 if (rrdcgiDebugStderr)
1513 fprintf(stderr, "%s: %s\n", result[i]->name,
1516 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1517 result[i]->name, result[i]->value);
1520 } else { /* There is already such a name, suppose a mutiple field */
1522 len = strlen(result[k]->value) + (ip - esp) + 2;
1523 if ((sptr = (char *) calloc(len, sizeof(char))) == NULL)
1525 snprintf(sptr, len, "%s\n%s", result[k]->value, cp);
1526 free(result[k]->value);
1527 result[k]->value = rrdcgiDecodeString(sptr);
1537 * Read from stdin if no string is provided via CGI. Variables that
1538 * doesn't have a value associated with it doesn't get stored.
1546 vars = rrdcgiReadVariables();
1551 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1558 char *rrdcgiGetValue(
1564 if (!parms || !parms->vars)
1566 for (i = 0; parms->vars[i]; i++)
1567 if (!strcmp(name, parms->vars[i]->name)) {
1568 if (rrdcgiDebugLevel > 0) {
1569 if (rrdcgiDebugStderr)
1570 fprintf(stderr, "%s found as %s\n", name,
1571 parms->vars[i]->value);
1573 printf("%s found as %s<br>\n", name,
1574 parms->vars[i]->value);
1576 return parms->vars[i]->value;
1578 if (rrdcgiDebugLevel) {
1579 if (rrdcgiDebugStderr)
1580 fprintf(stderr, "%s not found\n", name);
1582 printf("%s not found<br>\n", name);
1587 void rrdcgiFreeList(
1592 for (i = 0; list[i] != NULL; i++)
1605 for (i = 0; parms->vars[i]; i++) {
1606 if (parms->vars[i]->name)
1607 free(parms->vars[i]->name);
1608 if (parms->vars[i]->value)
1609 free(parms->vars[i]->value);
1610 free(parms->vars[i]);
1616 if (rrdcgiHeaderString) {
1617 free(rrdcgiHeaderString);
1618 rrdcgiHeaderString = NULL;