1 /*****************************************************************************
2 * RRDtool 1.2.12 Copyright by Tobi Oetiker, 1997-2005
3 *****************************************************************************
4 * rrd_cgi.c RRD Web Page Generator
5 *****************************************************************************/
11 /*#define DEBUG_PARSER
14 typedef struct var_s {
18 typedef struct cgi_s {
22 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
23 and replace by result of arg[2] call */
24 int parse(char **, long, char *, char *(*)(long , const char **));
26 /**************************************************/
27 /* tag replacers ... they are called from parse */
28 /* through function pointers */
29 /**************************************************/
31 /* return cgi var named arg[0] */
32 char* cgiget(long , const char **);
34 /* return a quoted cgi var named arg[0] */
35 char* cgigetq(long , const char **);
37 /* return a quoted and sanitized cgi variable */
38 char* cgigetqp(long , const char **);
40 /* call rrd_graph and insert appropriate image tag */
41 char* drawgraph(long, const char **);
43 /* return PRINT functions from last rrd_graph call */
44 char* drawprint(long, const char **);
46 /* pretty-print the <last></last> value for some.rrd via strftime() */
47 char* printtimelast(long, const char **);
49 /* pretty-print current time */
50 char* printtimenow(long, const char **);
52 /* set an environment variable */
53 char* rrdsetenv(long, const char **);
55 /* get an environment variable */
56 char* rrdgetenv(long, const char **);
58 /* include the named file at this point */
59 char* includefile(long, const char **);
61 /* for how long is the output of the cgi valid ? */
62 char* rrdgoodfor(long, const char **);
64 /* return rrdcgi version string */
65 char* rrdgetinternal(long, const char **);
67 char* rrdstrip(char *buf);
68 char* scanargs(char *line, int *argc, char ***args);
70 /* format at-time specified times using strftime */
71 char* printstrftime(long, const char**);
73 /** HTTP protocol needs special format, and GMT time **/
74 char *http_time(time_t *);
76 /* return a pointer to newly allocated copy of this string */
77 char *stralloc(const char *);
79 /* global variable for rrdcgi */
84 * Prints a valid CGI Header (Content-type...) etc.
86 void rrdcgiHeader(void);
92 char *rrdcgiDecodeString(char *text);
96 * Set/unsets debugging
98 void rrdcgiDebug(int level, int where);
102 * Reads in variables set via POST or stdin.
104 s_cgi *rrdcgiInit (void);
108 * Returns the value of the specified variable or NULL if it's empty
111 char *rrdcgiGetValue (s_cgi *parms, const char *name);
115 * Frees a list as returned by rrdcgiGetVariables()
117 void rrdcgiFreeList (char **list);
121 * Frees the internal data structures
123 void rrdcgiFree (s_cgi *parms);
125 /* rrdcgiReadVariables()
127 * Read from stdin if no string is provided via CGI. Variables that
128 * doesn't have a value associated with it doesn't get stored.
130 s_var **rrdcgiReadVariables(void);
133 int rrdcgiDebugLevel = 0;
134 int rrdcgiDebugStderr = 1;
135 char *rrdcgiHeaderString = NULL;
136 char *rrdcgiType = NULL;
138 /* rrd interface to the variable functions {put,get}var() */
139 char* rrdgetvar(long argc, const char **args);
140 char* rrdsetvar(long argc, const char **args);
141 char* rrdsetvarconst(long argc, const char **args);
144 /* variable store: put/get key-value pairs */
145 static int initvar();
146 static void donevar();
147 static const char* getvar(const char* varname);
148 static const char* putvar(const char* name, const char* value, int is_const);
150 /* key value pair that makes up an entry in the variable store */
153 int is_const; /* const variable or not */
154 const char* name; /* variable name */
155 const char* value; /* variable value */
158 /* the variable heap:
159 start with a heapsize of 10 variables */
160 #define INIT_VARSTORE_SIZE 10
161 static vardata* varheap = NULL;
162 static size_t varheap_size = 0;
164 /* allocate and initialize variable heap */
168 varheap = (vardata*)malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
169 if (varheap == NULL) {
170 fprintf(stderr, "ERROR: unable to initialize variable store\n");
173 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
174 varheap_size = INIT_VARSTORE_SIZE;
178 /* cleanup: free allocated memory */
184 for (i=0; i<(int)varheap_size; i++) {
185 if (varheap[i].name) {
186 free((char*)varheap[i].name);
188 if (varheap[i].value) {
189 free((char*)varheap[i].value);
196 /* Get a variable from the variable store.
197 Return NULL in case the requested variable was not found. */
199 getvar(const char* name)
202 for (i=0; i<(int)varheap_size && varheap[i].name; i++) {
203 if (0 == strcmp(name, varheap[i].name)) {
205 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
207 return varheap[i].value;
211 printf("<!-- getvar(%s) -> Not found-->\n", name);
216 /* Put a variable into the variable store. If a variable by that
217 name exists, it's value is overwritten with the new value unless it was
218 marked as 'const' (initialized by RRD::SETCONSTVAR).
219 Returns a copy the newly allocated value on success, NULL on error. */
221 putvar(const char* name, const char* value, int is_const)
224 for (i=0; i < (int)varheap_size && varheap[i].name; i++) {
225 if (0 == strcmp(name, varheap[i].name)) {
226 /* overwrite existing entry */
227 if (varheap[i].is_const) {
229 printf("<!-- setver(%s, %s): not assigning: "
230 "const variable -->\n", name, value);
232 return varheap[i].value;
235 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
236 name, value, varheap[i].value);
238 /* make it possible to promote a variable to readonly */
239 varheap[i].is_const = is_const;
240 free((char*)varheap[i].value);
241 varheap[i].value = stralloc(value);
242 return varheap[i].value;
246 /* no existing variable found by that name, add it */
247 if (i == (int)varheap_size) {
248 /* ran out of heap: resize heap to double size */
249 size_t new_size = varheap_size * 2;
250 varheap = (vardata*)(realloc(varheap, sizeof(vardata) * new_size));
252 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
255 /* initialize newly allocated memory */;
256 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
257 varheap_size = new_size;
259 varheap[i].is_const = is_const;
260 varheap[i].name = stralloc(name);
261 varheap[i].value = stralloc(value);
264 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
266 return varheap[i].value;
269 /* expand those RRD:* directives that can be used recursivly */
271 rrd_expand_vars(char* buffer)
276 printf("expanding variables in '%s'\n", buffer);
279 for (i=0; buffer[i]; i++) {
280 if (buffer[i] != '<')
282 parse(&buffer, i, "<RRD::CV", cgiget);
283 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
284 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
285 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
286 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
287 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
288 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
289 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
290 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
295 static long goodfor=0;
296 static char **calcpr=NULL;
297 static void calfree (void){
300 for(i=0;calcpr[i];i++){
311 /* create freeable version of the string */
312 char * stralloc(const char *str){
317 nstr = malloc((strlen(str)+1));
322 int main(int argc, char *argv[]) {
325 char *server_url = NULL;
328 #ifdef MUST_DISABLE_SIGFPE
329 signal(SIGFPE,SIG_IGN);
331 #ifdef MUST_DISABLE_FPMASK
334 optind = 0; opterr = 0; /* initialize getopt */
336 /* what do we get for cmdline arguments?
338 printf("%d-'%s'\n",i,argv[i]); */
340 static struct option long_options[] = {
341 { "filter", no_argument, 0, 'f' },
344 int option_index = 0;
346 opt = getopt_long(argc, argv, "f", long_options, &option_index);
356 printf("unknown commandline option '%s'\n",argv[optind-1]);
363 rrdcgiArg = rrdcgiInit();
364 server_url = getenv("SERVER_URL");
367 /* make sure we have one extra argument,
368 if there are others, we do not care Apache gives several */
370 /* if ( (optind != argc-2
371 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
372 && optind != argc-1) { */
374 if ( optind >= argc ) {
375 fprintf(stderr, "ERROR: expected a filename\n");
378 length = readfile(argv[optind], &buffer, 1);
381 if(rrd_test_error()) {
382 fprintf(stderr, "ERROR: %s\n",rrd_get_error());
386 /* initialize variable heap */
390 /* some fake header for testing */
391 printf ("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
395 /* expand rrd directives in buffer recursivly */
396 for (i=0; buffer[i]; i++) {
397 if (buffer[i] != '<')
400 parse(&buffer, i, "<RRD::CV", cgiget);
401 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
402 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
403 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
405 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
406 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
407 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
408 parse(&buffer, i, "<RRD::INCLUDE", includefile);
409 parse(&buffer, i, "<RRD::PRINT", drawprint);
410 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
411 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
412 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
413 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
414 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
415 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
416 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
420 printf ("Content-Type: text/html\n"
421 "Content-Length: %d\n",
424 if (labs(goodfor) > 0) {
427 printf("Last-Modified: %s\n", http_time(&now));
428 now += labs(goodfor);
429 printf("Expires: %s\n", http_time(&now));
431 printf("Refresh: %ld\n", labs(goodfor));
438 printf("%s", buffer);
449 /* remove occurrences of .. this is a general measure to make
450 paths which came in via cgi do not go UP ... */
452 char* rrdsetenv(long argc, const char **args) {
454 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
456 return stralloc("[ERROR: allocating setenv buffer]");
458 sprintf(xyz, "%s=%s", args[0], args[1]);
459 if(putenv(xyz) == -1) {
461 return stralloc("[ERROR: failed to do putenv]");
465 return stralloc("[ERROR: setenv failed because not enough "
466 "arguments were defined]");
469 /* rrd interface to the variable function putvar() */
471 rrdsetvar(long argc, const char **args)
475 const char* result = putvar(args[0], args[1], 0 /* not const */);
477 /* setvar does not return the value set */
480 return stralloc("[ERROR: putvar failed]");
482 return stralloc("[ERROR: putvar failed because not enough arguments "
486 /* rrd interface to the variable function putvar() */
488 rrdsetvarconst(long argc, const char **args)
492 const char* result = putvar(args[0], args[1], 1 /* const */);
494 /* setvar does not return the value set */
497 return stralloc("[ERROR: putvar failed]");
499 return stralloc("[ERROR: putvar failed because not enough arguments "
503 char* rrdgetenv(long argc, const char **args) {
507 return stralloc("[ERROR: getenv failed because it did not "
508 "get 1 argument only]");
510 envvar = getenv(args[0]);
512 return stralloc(envvar);
514 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
515 return stralloc(buf);
519 char* rrdgetvar(long argc, const char **args) {
523 return stralloc("[ERROR: getvar failed because it did not "
524 "get 1 argument only]");
526 value = getvar(args[0]);
528 return stralloc(value);
530 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
531 return stralloc(buf);
535 char* rrdgoodfor(long argc, const char **args){
537 goodfor = atol(args[0]);
539 return stralloc("[ERROR: goodfor expected 1 argument]");
543 return stralloc("[ERROR: goodfor value must not be 0]");
549 char* rrdgetinternal(long argc, const char **args){
551 if( strcasecmp( args[0], "VERSION") == 0) {
552 return stralloc(PACKAGE_VERSION);
553 } else if( strcasecmp( args[0], "COPYRIGHT") == 0) {
554 return stralloc(PACKAGE_COPYRIGHT);
555 } else if( strcasecmp( args[0], "COMPILETIME") == 0) {
556 return stralloc(__DATE__ " " __TIME__);
557 } else if( strcasecmp( args[0], "OS") == 0) {
560 return stralloc("[ERROR: internal unknown argument]");
563 return stralloc("[ERROR: internal expected 1 argument]");
567 /* Format start or end times using strftime. We always need both the
568 * start and end times, because, either might be relative to the other.
570 #define MAX_STRFTIME_SIZE 256
571 char* printstrftime(long argc, const char **args){
572 struct rrd_time_value start_tv, end_tv;
573 char *parsetime_error = NULL;
574 char formatted[MAX_STRFTIME_SIZE];
576 time_t start_tmp, end_tmp;
578 /* Make sure that we were given the right number of args */
580 rrd_set_error( "wrong number of args %d", argc);
584 /* Init start and end time */
585 parsetime("end-24h", &start_tv);
586 parsetime("now", &end_tv);
588 /* Parse the start and end times we were given */
589 if( (parsetime_error = parsetime( args[1], &start_tv))) {
590 rrd_set_error( "start time: %s", parsetime_error);
593 if( (parsetime_error = parsetime( args[2], &end_tv))) {
594 rrd_set_error( "end time: %s", parsetime_error);
597 if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
601 /* Do we do the start or end */
602 if( strcasecmp( args[0], "START") == 0) {
603 the_tm = localtime( &start_tmp);
605 else if( strcasecmp( args[0], "END") == 0) {
606 the_tm = localtime( &end_tmp);
609 rrd_set_error( "start/end not found in '%s'", args[0]);
614 if( strftime( formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
615 return( stralloc( formatted));
618 rrd_set_error( "strftime failed");
623 char* includefile(long argc, const char **args){
626 const char* filename = args[0];
627 readfile(filename, &buffer, 0);
628 if (rrd_test_error()) {
629 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE));
630 sprintf(err, "[ERROR: %s]",rrd_get_error());
639 return stralloc("[ERROR: No Inclue file defined]");
643 /* make a copy of buf and replace open/close brackets with '_' */
644 char* rrdstrip(char *buf) {
649 /* make a copy of the buffer */
657 if (*p == '<' || *p == '>') {
665 char* cgigetq(long argc, const char **args){
667 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
671 if (buf==NULL) return NULL;
673 for(c=buf;*c != '\0';c++)
675 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
676 perror("Malloc Buffer");
697 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
700 /* remove occurrences of .. this is a general measure to make
701 paths which came in via cgi do not go UP ... */
703 char* cgigetqp(long argc, const char **args){
711 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
714 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
720 buf2 = malloc(strlen(buf)+1);
723 perror("cgigetqp(): Malloc Path Buffer");
732 /* prevent mallicious paths from entering the system */
733 if (p[0] == '.' && p[1] == '.')
748 /* Make sure the path is relative, e.g. does not start with '/' */
759 char* cgiget(long argc, const char **args){
761 return rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
763 return stralloc("[ERROR: not enough arguments for RRD::CV]");
768 char* drawgraph(long argc, const char **args){
772 if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
774 args[argc++] = "--imginfo";
775 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
778 if( rrd_graph(argc+1, (char **) args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
779 return stralloc(calcpr[0]);
781 if (rrd_test_error()) {
782 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
783 sprintf(err, "[ERROR: %s]",rrd_get_error());
792 char* drawprint(long argc, const char **args){
793 if (argc==1 && calcpr){
795 while (calcpr[i] != NULL) i++; /*determine number lines in calcpr*/
796 if (atol(args[0])<i-1)
797 return stralloc(calcpr[atol(args[0])+1]);
799 return stralloc("[ERROR: RRD::PRINT argument error]");
802 char* printtimelast(long argc, const char **args) {
809 return stralloc("[ERROR: allocating strftime buffer]");
811 last = rrd_last(argc+1, (char **) args-1);
812 if (rrd_test_error()) {
813 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
814 sprintf(err, "[ERROR: %s]",rrd_get_error());
818 tm_last = *localtime(&last);
819 strftime(buf,254,args[1],&tm_last);
823 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
825 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
828 char* printtimenow(long argc, const char **args) {
829 time_t now = time(NULL);
835 return stralloc("[ERROR: allocating strftime buffer]");
837 tm_now = *localtime(&now);
838 strftime(buf,254,args[0],&tm_now);
842 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
844 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
847 /* Scan buffer until an unescaped '>' arives.
848 * Update argument array with arguments found.
849 * Return end cursor where parsing stopped, or NULL in case of failure.
852 * To allow nested constructs, we call rrd_expand_vars() for arguments
853 * that contain RRD::x directives. These introduce a small memory leak
854 * since we have to stralloc the arguments the way parse() works.
857 scanargs(char *line, int *argument_count, char ***arguments)
859 char *getP; /* read cursor */
860 char *putP; /* write cursor */
861 char Quote; /* type of quote if in quoted string, 0 otherwise */
862 int tagcount; /* open tag count */
863 int in_arg; /* if we currently are parsing an argument or not */
864 int argsz; /* argument array size */
865 int curarg_contains_rrd_directives;
867 /* local array of arguments while parsing */
872 printf("<-- scanargs(%s) -->\n", line);
878 /* create initial argument array of char pointers */
880 argv = (char **)malloc(argsz * sizeof(char *));
885 /* skip leading blanks */
886 while (isspace((int)*line)) {
897 curarg_contains_rrd_directives = 0;
899 /* start parsing 'line' for arguments */
902 unsigned char c = *getP++;
904 if (c == '>' && !Quote && !tagcount) {
905 /* this is our closing tag, quit scanning */
909 /* remove all special chars */
917 if (Quote || tagcount) {
918 /* copy quoted/tagged (=RRD expanded) string */
923 /* end argument string */
926 if (curarg_contains_rrd_directives) {
927 argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
928 curarg_contains_rrd_directives = 0;
933 case '"': /* Fall through */
939 /* copy quoted string */
944 /* reference start of argument string in argument array */
954 /* start new argument */
965 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
966 curarg_contains_rrd_directives = 1;
973 /* check if our argument array is still large enough */
975 /* resize argument array */
977 argv = rrd_realloc(argv, argsz * sizeof(char *));
984 /* terminate last argument found */
986 if (curarg_contains_rrd_directives) {
987 argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
993 printf("<-- arguments found [%d]\n", argc);
994 for (n=0; n<argc; n++) {
995 printf("arg %02d: '%s'\n", n, argv[n]);
999 printf("<!-- No arguments found -->\n");
1003 /* update caller's notion of the argument array and it's size */
1005 *argument_count = argc;
1011 /* Return new scanning cursor:
1012 pointer to char after closing bracket */
1018 * Parse(): scan current portion of buffer for given tag.
1019 * If found, parse tag arguments and call 'func' for it.
1020 * The result of func is inserted at the current position
1025 char **buf, /* buffer */
1026 long i, /* offset in buffer */
1027 char *tag, /* tag to handle */
1028 char *(*func)(long , const char **) /* function to call for 'tag' */
1031 /* the name of the vairable ... */
1038 size_t taglen = strlen(tag);
1040 /* Current position in buffer should start with 'tag' */
1041 if (strncmp((*buf)+i, tag, taglen) != 0) {
1044 /* .. and match exactly (a whitespace following 'tag') */
1045 if (! isspace(*((*buf) + i + taglen)) ) {
1050 printf("parse(): handling tag '%s'\n", tag);
1053 /* Scan for arguments following the tag;
1054 scanargs() puts \0 into *buf ... so after scanargs it is probably
1055 not a good time to use strlen on buf */
1056 end = scanargs((*buf) + i + taglen, &argc, &args);
1059 /* got arguments, call function for 'tag' with arguments */
1060 val = func(argc, (const char **) args);
1065 /* unable to parse arguments, undo 0-termination by scanargs */
1066 for (; argc > 0; argc--) {
1067 *((args[argc-1])-1) = ' ';
1070 /* next call, try parsing at current offset +1 */
1071 end = (*buf) + i + 1;
1073 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1074 " Check original file. This may have been altered "
1075 "by parsing.]\n\n");
1078 /* remember offset where we have to continue parsing */
1079 end_offset = end - (*buf);
1083 valln = strlen(val);
1086 /* Optionally resize buffer to hold the replacement value:
1087 Calculating the new length of the buffer is simple. add current
1088 buffer pos (i) to length of string after replaced tag to length
1089 of replacement string and add 1 for the final zero ... */
1090 if (end - (*buf) < (i + valln)) {
1091 /* make sure we do not shrink the mallocd block */
1092 size_t newbufsize = i + strlen(end) + valln + 1;
1093 *buf = rrd_realloc(*buf, newbufsize);
1096 perror("Realoc buf:");
1101 /* Update new end pointer:
1102 make sure the 'end' pointer gets moved along with the
1103 buf pointer when realloc moves memory ... */
1104 end = (*buf) + end_offset;
1106 /* splice the variable:
1107 step 1. Shift pending data to make room for 'val' */
1108 memmove((*buf) + i + valln, end, strlen(end) + 1);
1110 /* step 2. Insert val */
1112 memmove((*buf)+i, val, valln);
1115 return (valln > 0 ? valln-1: valln);
1119 http_time(time_t *now) {
1121 static char buf[60];
1123 tmptime=gmtime(now);
1124 strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
1128 void rrdcgiHeader(void)
1131 printf ("Content-type: %s\n", rrdcgiType);
1133 printf ("Content-type: text/html\n");
1134 if (rrdcgiHeaderString)
1135 printf ("%s", rrdcgiHeaderString);
1139 void rrdcgiDebug(int level, int where)
1142 rrdcgiDebugLevel = level;
1144 rrdcgiDebugLevel = 0;
1146 rrdcgiDebugStderr = 0;
1148 rrdcgiDebugStderr = 1;
1151 char *rrdcgiDecodeString(char *text)
1155 for (cp=text,xp=text; *cp; cp++) {
1157 if (strchr("0123456789ABCDEFabcdef", *(cp+1))
1158 && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
1159 if (islower(*(cp+1)))
1160 *(cp+1) = toupper(*(cp+1));
1161 if (islower(*(cp+2)))
1162 *(cp+2) = toupper(*(cp+2));
1163 *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
1164 + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
1171 memset(xp, 0, cp-xp);
1175 /* rrdcgiReadVariables()
1177 * Read from stdin if no string is provided via CGI. Variables that
1178 * doesn't have a value associated with it doesn't get stored.
1180 s_var **rrdcgiReadVariables(void)
1185 char *cp, *ip, *esp, *sptr;
1190 cp = getenv("REQUEST_METHOD");
1191 ip = getenv("CONTENT_LENGTH");
1193 if (cp && !strcmp(cp, "POST")) {
1196 if ((line = (char *)malloc (length+2)) == NULL)
1198 fgets(line, length+1, stdin);
1201 } else if (cp && !strcmp(cp, "GET")) {
1202 esp = getenv("QUERY_STRING");
1203 if (esp && strlen(esp)) {
1204 if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
1206 sprintf (line, "%s", esp);
1211 printf ("(offline mode: enter name=value pairs on standard input)\n");
1212 memset (tmp, 0, sizeof(tmp));
1213 while((cp = fgets (tmp, 100, stdin)) != NULL) {
1215 if (tmp[strlen(tmp)-1] == '\n')
1216 tmp[strlen(tmp)-1] = '&';
1218 length += strlen(tmp);
1219 len = (length+1) * sizeof(char);
1220 if ((line = (char *)realloc (line, len)) == NULL)
1224 length = strlen(tmp);
1225 len = (length+1) * sizeof(char);
1226 if ((line = (char *)malloc (len)) == NULL)
1228 memset (line, 0, len);
1232 memset (tmp, 0, sizeof(tmp));
1236 if (line[strlen(line)-1] == '&')
1237 line[strlen(line)-1] = '\0';
1241 * From now on all cgi variables are stored in the variable line
1242 * and look like foo=bar&foobar=barfoo&foofoo=
1245 if (rrdcgiDebugLevel > 0) {
1246 if (rrdcgiDebugStderr)
1247 fprintf (stderr, "Received cgi input: %s\n", line);
1249 printf ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n", line);
1252 for (cp=line; *cp; cp++)
1257 for (numargs=1,cp=line; *cp; cp++)
1258 if (*cp == '&') numargs++;
1261 if (rrdcgiDebugLevel > 0) {
1262 if (rrdcgiDebugStderr)
1263 fprintf (stderr, "%d cgi variables found.\n", numargs);
1265 printf ("%d cgi variables found.<br>\n", numargs);
1268 len = (numargs+1) * sizeof(s_var *);
1269 if ((result = (s_var **)malloc (len)) == NULL)
1271 memset (result, 0, len);
1276 if ((ip = (char *)strchr(cp, '&')) != NULL) {
1279 ip = cp + strlen(cp);
1281 if ((esp=(char *)strchr(cp, '=')) == NULL) {
1293 /* try to find out if there's already such a variable */
1294 for (k=0; k<i && (strncmp (result[k]->name,cp, esp-cp) || !(strlen (result[k]->name) == esp-cp)); k++);
1296 if (k == i) { /* No such variable yet */
1297 if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
1299 if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
1301 memset (result[i]->name, 0, esp-cp+1);
1302 strncpy(result[i]->name, cp, esp-cp);
1304 if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
1306 memset (result[i]->value, 0, ip-esp+1);
1307 strncpy(result[i]->value, cp, ip-esp);
1308 result[i]->value = rrdcgiDecodeString(result[i]->value);
1309 if (rrdcgiDebugLevel) {
1310 if (rrdcgiDebugStderr)
1311 fprintf (stderr, "%s: %s\n", result[i]->name, result[i]->value);
1313 printf ("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n", result[i]->name, result[i]->value);
1316 } else { /* There is already such a name, suppose a mutiple field */
1318 len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
1319 if ((sptr = (char *)malloc(len)) == NULL)
1321 memset (sptr, 0, len);
1322 sprintf (sptr, "%s\n", result[k]->value);
1323 strncat(sptr, cp, ip-esp);
1324 free(result[k]->value);
1325 result[k]->value = rrdcgiDecodeString (sptr);
1335 * Read from stdin if no string is provided via CGI. Variables that
1336 * doesn't have a value associated with it doesn't get stored.
1338 s_cgi *rrdcgiInit(void)
1343 vars = rrdcgiReadVariables();
1348 if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
1355 char *rrdcgiGetValue(s_cgi *parms, const char *name)
1359 if (!parms || !parms->vars)
1361 for (i=0;parms->vars[i]; i++)
1362 if (!strcmp(name,parms->vars[i]->name)) {
1363 if (rrdcgiDebugLevel > 0) {
1364 if (rrdcgiDebugStderr)
1365 fprintf (stderr, "%s found as %s\n", name, parms->vars[i]->value);
1367 printf ("%s found as %s<br>\n", name, parms->vars[i]->value);
1369 return parms->vars[i]->value;
1371 if (rrdcgiDebugLevel) {
1372 if (rrdcgiDebugStderr)
1373 fprintf (stderr, "%s not found\n", name);
1375 printf ("%s not found<br>\n", name);
1380 void rrdcgiFreeList (char **list)
1384 for (i=0; list[i] != NULL; i++)
1389 void rrdcgiFree (s_cgi *parms)
1396 for (i=0;parms->vars[i]; i++) {
1397 if (parms->vars[i]->name)
1398 free (parms->vars[i]->name);
1399 if (parms->vars[i]->value)
1400 free (parms->vars[i]->value);
1401 free (parms->vars[i]);
1407 if (rrdcgiHeaderString) {
1408 free (rrdcgiHeaderString);
1409 rrdcgiHeaderString = NULL;