1 /*****************************************************************************
2 * RRDtool 1.2.13 Copyright by Tobi Oetiker, 1997-2006
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], "COMPILETIME") == 0) {
554 return stralloc(__DATE__ " " __TIME__);
556 return stralloc("[ERROR: internal unknown argument]");
559 return stralloc("[ERROR: internal expected 1 argument]");
563 /* Format start or end times using strftime. We always need both the
564 * start and end times, because, either might be relative to the other.
566 #define MAX_STRFTIME_SIZE 256
567 char* printstrftime(long argc, const char **args){
568 struct rrd_time_value start_tv, end_tv;
569 char *parsetime_error = NULL;
570 char formatted[MAX_STRFTIME_SIZE];
572 time_t start_tmp, end_tmp;
574 /* Make sure that we were given the right number of args */
576 rrd_set_error( "wrong number of args %d", argc);
580 /* Init start and end time */
581 parsetime("end-24h", &start_tv);
582 parsetime("now", &end_tv);
584 /* Parse the start and end times we were given */
585 if( (parsetime_error = parsetime( args[1], &start_tv))) {
586 rrd_set_error( "start time: %s", parsetime_error);
589 if( (parsetime_error = parsetime( args[2], &end_tv))) {
590 rrd_set_error( "end time: %s", parsetime_error);
593 if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
597 /* Do we do the start or end */
598 if( strcasecmp( args[0], "START") == 0) {
599 the_tm = localtime( &start_tmp);
601 else if( strcasecmp( args[0], "END") == 0) {
602 the_tm = localtime( &end_tmp);
605 rrd_set_error( "start/end not found in '%s'", args[0]);
610 if( strftime( formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
611 return( stralloc( formatted));
614 rrd_set_error( "strftime failed");
619 char* includefile(long argc, const char **args){
622 const char* filename = args[0];
623 readfile(filename, &buffer, 0);
624 if (rrd_test_error()) {
625 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE));
626 sprintf(err, "[ERROR: %s]",rrd_get_error());
635 return stralloc("[ERROR: No Inclue file defined]");
639 /* make a copy of buf and replace open/close brackets with '_' */
640 char* rrdstrip(char *buf) {
645 /* make a copy of the buffer */
653 if (*p == '<' || *p == '>') {
661 char* cgigetq(long argc, const char **args){
663 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
667 if (buf==NULL) return NULL;
669 for(c=buf;*c != '\0';c++)
671 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
672 perror("Malloc Buffer");
693 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
696 /* remove occurrences of .. this is a general measure to make
697 paths which came in via cgi do not go UP ... */
699 char* cgigetqp(long argc, const char **args){
707 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
710 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
716 buf2 = malloc(strlen(buf)+1);
719 perror("cgigetqp(): Malloc Path Buffer");
728 /* prevent mallicious paths from entering the system */
729 if (p[0] == '.' && p[1] == '.')
744 /* Make sure the path is relative, e.g. does not start with '/' */
755 char* cgiget(long argc, const char **args){
757 return rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
759 return stralloc("[ERROR: not enough arguments for RRD::CV]");
764 char* drawgraph(long argc, const char **args){
768 if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
770 args[argc++] = "--imginfo";
771 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
774 if( rrd_graph(argc+1, (char **) args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
775 return stralloc(calcpr[0]);
777 if (rrd_test_error()) {
778 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
779 sprintf(err, "[ERROR: %s]",rrd_get_error());
788 char* drawprint(long argc, const char **args){
789 if (argc==1 && calcpr){
791 while (calcpr[i] != NULL) i++; /*determine number lines in calcpr*/
792 if (atol(args[0])<i-1)
793 return stralloc(calcpr[atol(args[0])+1]);
795 return stralloc("[ERROR: RRD::PRINT argument error]");
798 char* printtimelast(long argc, const char **args) {
805 return stralloc("[ERROR: allocating strftime buffer]");
807 last = rrd_last(argc+1, (char **) args-1);
808 if (rrd_test_error()) {
809 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
810 sprintf(err, "[ERROR: %s]",rrd_get_error());
814 tm_last = *localtime(&last);
815 strftime(buf,254,args[1],&tm_last);
819 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
821 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
824 char* printtimenow(long argc, const char **args) {
825 time_t now = time(NULL);
831 return stralloc("[ERROR: allocating strftime buffer]");
833 tm_now = *localtime(&now);
834 strftime(buf,254,args[0],&tm_now);
838 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
840 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
843 /* Scan buffer until an unescaped '>' arives.
844 * Update argument array with arguments found.
845 * Return end cursor where parsing stopped, or NULL in case of failure.
848 * To allow nested constructs, we call rrd_expand_vars() for arguments
849 * that contain RRD::x directives. These introduce a small memory leak
850 * since we have to stralloc the arguments the way parse() works.
853 scanargs(char *line, int *argument_count, char ***arguments)
855 char *getP; /* read cursor */
856 char *putP; /* write cursor */
857 char Quote; /* type of quote if in quoted string, 0 otherwise */
858 int tagcount; /* open tag count */
859 int in_arg; /* if we currently are parsing an argument or not */
860 int argsz; /* argument array size */
861 int curarg_contains_rrd_directives;
863 /* local array of arguments while parsing */
868 printf("<-- scanargs(%s) -->\n", line);
874 /* create initial argument array of char pointers */
876 argv = (char **)malloc(argsz * sizeof(char *));
881 /* skip leading blanks */
882 while (isspace((int)*line)) {
893 curarg_contains_rrd_directives = 0;
895 /* start parsing 'line' for arguments */
898 unsigned char c = *getP++;
900 if (c == '>' && !Quote && !tagcount) {
901 /* this is our closing tag, quit scanning */
905 /* remove all special chars */
913 if (Quote || tagcount) {
914 /* copy quoted/tagged (=RRD expanded) string */
919 /* end argument string */
922 if (curarg_contains_rrd_directives) {
923 argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
924 curarg_contains_rrd_directives = 0;
929 case '"': /* Fall through */
935 /* copy quoted string */
940 /* reference start of argument string in argument array */
950 /* start new argument */
961 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
962 curarg_contains_rrd_directives = 1;
969 /* check if our argument array is still large enough */
971 /* resize argument array */
973 argv = rrd_realloc(argv, argsz * sizeof(char *));
980 /* terminate last argument found */
982 if (curarg_contains_rrd_directives) {
983 argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
989 printf("<-- arguments found [%d]\n", argc);
990 for (n=0; n<argc; n++) {
991 printf("arg %02d: '%s'\n", n, argv[n]);
995 printf("<!-- No arguments found -->\n");
999 /* update caller's notion of the argument array and it's size */
1001 *argument_count = argc;
1007 /* Return new scanning cursor:
1008 pointer to char after closing bracket */
1014 * Parse(): scan current portion of buffer for given tag.
1015 * If found, parse tag arguments and call 'func' for it.
1016 * The result of func is inserted at the current position
1021 char **buf, /* buffer */
1022 long i, /* offset in buffer */
1023 char *tag, /* tag to handle */
1024 char *(*func)(long , const char **) /* function to call for 'tag' */
1027 /* the name of the vairable ... */
1034 size_t taglen = strlen(tag);
1036 /* Current position in buffer should start with 'tag' */
1037 if (strncmp((*buf)+i, tag, taglen) != 0) {
1040 /* .. and match exactly (a whitespace following 'tag') */
1041 if (! isspace(*((*buf) + i + taglen)) ) {
1046 printf("parse(): handling tag '%s'\n", tag);
1049 /* Scan for arguments following the tag;
1050 scanargs() puts \0 into *buf ... so after scanargs it is probably
1051 not a good time to use strlen on buf */
1052 end = scanargs((*buf) + i + taglen, &argc, &args);
1055 /* got arguments, call function for 'tag' with arguments */
1056 val = func(argc, (const char **) args);
1061 /* unable to parse arguments, undo 0-termination by scanargs */
1062 for (; argc > 0; argc--) {
1063 *((args[argc-1])-1) = ' ';
1066 /* next call, try parsing at current offset +1 */
1067 end = (*buf) + i + 1;
1069 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1070 " Check original file. This may have been altered "
1071 "by parsing.]\n\n");
1074 /* remember offset where we have to continue parsing */
1075 end_offset = end - (*buf);
1079 valln = strlen(val);
1082 /* Optionally resize buffer to hold the replacement value:
1083 Calculating the new length of the buffer is simple. add current
1084 buffer pos (i) to length of string after replaced tag to length
1085 of replacement string and add 1 for the final zero ... */
1086 if (end - (*buf) < (i + valln)) {
1087 /* make sure we do not shrink the mallocd block */
1088 size_t newbufsize = i + strlen(end) + valln + 1;
1089 *buf = rrd_realloc(*buf, newbufsize);
1092 perror("Realoc buf:");
1097 /* Update new end pointer:
1098 make sure the 'end' pointer gets moved along with the
1099 buf pointer when realloc moves memory ... */
1100 end = (*buf) + end_offset;
1102 /* splice the variable:
1103 step 1. Shift pending data to make room for 'val' */
1104 memmove((*buf) + i + valln, end, strlen(end) + 1);
1106 /* step 2. Insert val */
1108 memmove((*buf)+i, val, valln);
1111 return (valln > 0 ? valln-1: valln);
1115 http_time(time_t *now) {
1117 static char buf[60];
1119 tmptime=gmtime(now);
1120 strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
1124 void rrdcgiHeader(void)
1127 printf ("Content-type: %s\n", rrdcgiType);
1129 printf ("Content-type: text/html\n");
1130 if (rrdcgiHeaderString)
1131 printf ("%s", rrdcgiHeaderString);
1135 void rrdcgiDebug(int level, int where)
1138 rrdcgiDebugLevel = level;
1140 rrdcgiDebugLevel = 0;
1142 rrdcgiDebugStderr = 0;
1144 rrdcgiDebugStderr = 1;
1147 char *rrdcgiDecodeString(char *text)
1151 for (cp=text,xp=text; *cp; cp++) {
1153 if (strchr("0123456789ABCDEFabcdef", *(cp+1))
1154 && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
1155 if (islower(*(cp+1)))
1156 *(cp+1) = toupper(*(cp+1));
1157 if (islower(*(cp+2)))
1158 *(cp+2) = toupper(*(cp+2));
1159 *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
1160 + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
1167 memset(xp, 0, cp-xp);
1171 /* rrdcgiReadVariables()
1173 * Read from stdin if no string is provided via CGI. Variables that
1174 * doesn't have a value associated with it doesn't get stored.
1176 s_var **rrdcgiReadVariables(void)
1181 char *cp, *ip, *esp, *sptr;
1186 cp = getenv("REQUEST_METHOD");
1187 ip = getenv("CONTENT_LENGTH");
1189 if (cp && !strcmp(cp, "POST")) {
1192 if ((line = (char *)malloc (length+2)) == NULL)
1194 fgets(line, length+1, stdin);
1197 } else if (cp && !strcmp(cp, "GET")) {
1198 esp = getenv("QUERY_STRING");
1199 if (esp && strlen(esp)) {
1200 if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
1202 sprintf (line, "%s", esp);
1207 printf ("(offline mode: enter name=value pairs on standard input)\n");
1208 memset (tmp, 0, sizeof(tmp));
1209 while((cp = fgets (tmp, 100, stdin)) != NULL) {
1211 if (tmp[strlen(tmp)-1] == '\n')
1212 tmp[strlen(tmp)-1] = '&';
1214 length += strlen(tmp);
1215 len = (length+1) * sizeof(char);
1216 if ((line = (char *)realloc (line, len)) == NULL)
1220 length = strlen(tmp);
1221 len = (length+1) * sizeof(char);
1222 if ((line = (char *)malloc (len)) == NULL)
1224 memset (line, 0, len);
1228 memset (tmp, 0, sizeof(tmp));
1232 if (line[strlen(line)-1] == '&')
1233 line[strlen(line)-1] = '\0';
1237 * From now on all cgi variables are stored in the variable line
1238 * and look like foo=bar&foobar=barfoo&foofoo=
1241 if (rrdcgiDebugLevel > 0) {
1242 if (rrdcgiDebugStderr)
1243 fprintf (stderr, "Received cgi input: %s\n", line);
1245 printf ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n", line);
1248 for (cp=line; *cp; cp++)
1253 for (numargs=1,cp=line; *cp; cp++)
1254 if (*cp == '&') numargs++;
1257 if (rrdcgiDebugLevel > 0) {
1258 if (rrdcgiDebugStderr)
1259 fprintf (stderr, "%d cgi variables found.\n", numargs);
1261 printf ("%d cgi variables found.<br>\n", numargs);
1264 len = (numargs+1) * sizeof(s_var *);
1265 if ((result = (s_var **)malloc (len)) == NULL)
1267 memset (result, 0, len);
1272 if ((ip = (char *)strchr(cp, '&')) != NULL) {
1275 ip = cp + strlen(cp);
1277 if ((esp=(char *)strchr(cp, '=')) == NULL) {
1289 /* try to find out if there's already such a variable */
1290 for (k=0; k<i && (strncmp (result[k]->name,cp, esp-cp) || !(strlen (result[k]->name) == esp-cp)); k++);
1292 if (k == i) { /* No such variable yet */
1293 if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
1295 if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
1297 memset (result[i]->name, 0, esp-cp+1);
1298 strncpy(result[i]->name, cp, esp-cp);
1300 if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
1302 memset (result[i]->value, 0, ip-esp+1);
1303 strncpy(result[i]->value, cp, ip-esp);
1304 result[i]->value = rrdcgiDecodeString(result[i]->value);
1305 if (rrdcgiDebugLevel) {
1306 if (rrdcgiDebugStderr)
1307 fprintf (stderr, "%s: %s\n", result[i]->name, result[i]->value);
1309 printf ("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n", result[i]->name, result[i]->value);
1312 } else { /* There is already such a name, suppose a mutiple field */
1314 len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
1315 if ((sptr = (char *)malloc(len)) == NULL)
1317 memset (sptr, 0, len);
1318 sprintf (sptr, "%s\n", result[k]->value);
1319 strncat(sptr, cp, ip-esp);
1320 free(result[k]->value);
1321 result[k]->value = rrdcgiDecodeString (sptr);
1331 * Read from stdin if no string is provided via CGI. Variables that
1332 * doesn't have a value associated with it doesn't get stored.
1334 s_cgi *rrdcgiInit(void)
1339 vars = rrdcgiReadVariables();
1344 if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
1351 char *rrdcgiGetValue(s_cgi *parms, const char *name)
1355 if (!parms || !parms->vars)
1357 for (i=0;parms->vars[i]; i++)
1358 if (!strcmp(name,parms->vars[i]->name)) {
1359 if (rrdcgiDebugLevel > 0) {
1360 if (rrdcgiDebugStderr)
1361 fprintf (stderr, "%s found as %s\n", name, parms->vars[i]->value);
1363 printf ("%s found as %s<br>\n", name, parms->vars[i]->value);
1365 return parms->vars[i]->value;
1367 if (rrdcgiDebugLevel) {
1368 if (rrdcgiDebugStderr)
1369 fprintf (stderr, "%s not found\n", name);
1371 printf ("%s not found<br>\n", name);
1376 void rrdcgiFreeList (char **list)
1380 for (i=0; list[i] != NULL; i++)
1385 void rrdcgiFree (s_cgi *parms)
1392 for (i=0;parms->vars[i]; i++) {
1393 if (parms->vars[i]->name)
1394 free (parms->vars[i]->name);
1395 if (parms->vars[i]->value)
1396 free (parms->vars[i]->value);
1397 free (parms->vars[i]);
1403 if (rrdcgiHeaderString) {
1404 free (rrdcgiHeaderString);
1405 rrdcgiHeaderString = NULL;