1 /*****************************************************************************
2 * RRDtool 1.2.11 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 char* rrdstrip(char *buf);
65 char* scanargs(char *line, int *argc, char ***args);
67 /* format at-time specified times using strftime */
68 char* printstrftime(long, const char**);
70 /** HTTP protocol needs special format, and GMT time **/
71 char *http_time(time_t *);
73 /* return a pointer to newly allocated copy of this string */
74 char *stralloc(const char *);
76 /* global variable for rrdcgi */
81 * Prints a valid CGI Header (Content-type...) etc.
83 void rrdcgiHeader(void);
89 char *rrdcgiDecodeString(char *text);
93 * Set/unsets debugging
95 void rrdcgiDebug(int level, int where);
99 * Reads in variables set via POST or stdin.
101 s_cgi *rrdcgiInit (void);
105 * Returns the value of the specified variable or NULL if it's empty
108 char *rrdcgiGetValue (s_cgi *parms, const char *name);
112 * Frees a list as returned by rrdcgiGetVariables()
114 void rrdcgiFreeList (char **list);
118 * Frees the internal data structures
120 void rrdcgiFree (s_cgi *parms);
122 /* rrdcgiReadVariables()
124 * Read from stdin if no string is provided via CGI. Variables that
125 * doesn't have a value associated with it doesn't get stored.
127 s_var **rrdcgiReadVariables(void);
130 int rrdcgiDebugLevel = 0;
131 int rrdcgiDebugStderr = 1;
132 char *rrdcgiHeaderString = NULL;
133 char *rrdcgiType = NULL;
135 /* rrd interface to the variable functions {put,get}var() */
136 char* rrdgetvar(long argc, const char **args);
137 char* rrdsetvar(long argc, const char **args);
138 char* rrdsetvarconst(long argc, const char **args);
141 /* variable store: put/get key-value pairs */
142 static int initvar();
143 static void donevar();
144 static const char* getvar(const char* varname);
145 static const char* putvar(const char* name, const char* value, int is_const);
147 /* key value pair that makes up an entry in the variable store */
150 int is_const; /* const variable or not */
151 const char* name; /* variable name */
152 const char* value; /* variable value */
155 /* the variable heap:
156 start with a heapsize of 10 variables */
157 #define INIT_VARSTORE_SIZE 10
158 static vardata* varheap = NULL;
159 static size_t varheap_size = 0;
161 /* allocate and initialize variable heap */
165 varheap = (vardata*)malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
166 if (varheap == NULL) {
167 fprintf(stderr, "ERROR: unable to initialize variable store\n");
170 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
171 varheap_size = INIT_VARSTORE_SIZE;
175 /* cleanup: free allocated memory */
181 for (i=0; i<(int)varheap_size; i++) {
182 if (varheap[i].name) {
183 free((char*)varheap[i].name);
185 if (varheap[i].value) {
186 free((char*)varheap[i].value);
193 /* Get a variable from the variable store.
194 Return NULL in case the requested variable was not found. */
196 getvar(const char* name)
199 for (i=0; i<(int)varheap_size && varheap[i].name; i++) {
200 if (0 == strcmp(name, varheap[i].name)) {
202 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
204 return varheap[i].value;
208 printf("<!-- getvar(%s) -> Not found-->\n", name);
213 /* Put a variable into the variable store. If a variable by that
214 name exists, it's value is overwritten with the new value unless it was
215 marked as 'const' (initialized by RRD::SETCONSTVAR).
216 Returns a copy the newly allocated value on success, NULL on error. */
218 putvar(const char* name, const char* value, int is_const)
221 for (i=0; i < (int)varheap_size && varheap[i].name; i++) {
222 if (0 == strcmp(name, varheap[i].name)) {
223 /* overwrite existing entry */
224 if (varheap[i].is_const) {
226 printf("<!-- setver(%s, %s): not assigning: "
227 "const variable -->\n", name, value);
229 return varheap[i].value;
232 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
233 name, value, varheap[i].value);
235 /* make it possible to promote a variable to readonly */
236 varheap[i].is_const = is_const;
237 free((char*)varheap[i].value);
238 varheap[i].value = stralloc(value);
239 return varheap[i].value;
243 /* no existing variable found by that name, add it */
244 if (i == (int)varheap_size) {
245 /* ran out of heap: resize heap to double size */
246 size_t new_size = varheap_size * 2;
247 varheap = (vardata*)(realloc(varheap, sizeof(vardata) * new_size));
249 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
252 /* initialize newly allocated memory */;
253 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
254 varheap_size = new_size;
256 varheap[i].is_const = is_const;
257 varheap[i].name = stralloc(name);
258 varheap[i].value = stralloc(value);
261 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
263 return varheap[i].value;
266 /* expand those RRD:* directives that can be used recursivly */
268 rrd_expand_vars(char* buffer)
273 printf("expanding variables in '%s'\n", buffer);
276 for (i=0; buffer[i]; i++) {
277 if (buffer[i] != '<')
279 parse(&buffer, i, "<RRD::CV", cgiget);
280 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
281 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
282 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
283 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
284 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
285 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
286 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
291 static long goodfor=0;
292 static char **calcpr=NULL;
293 static void calfree (void){
296 for(i=0;calcpr[i];i++){
307 /* create freeable version of the string */
308 char * stralloc(const char *str){
313 nstr = malloc((strlen(str)+1));
318 int main(int argc, char *argv[]) {
321 char *server_url = NULL;
324 #ifdef MUST_DISABLE_SIGFPE
325 signal(SIGFPE,SIG_IGN);
327 #ifdef MUST_DISABLE_FPMASK
330 optind = 0; opterr = 0; /* initialize getopt */
332 /* what do we get for cmdline arguments?
334 printf("%d-'%s'\n",i,argv[i]); */
336 static struct option long_options[] = {
337 { "filter", no_argument, 0, 'f' },
340 int option_index = 0;
342 opt = getopt_long(argc, argv, "f", long_options, &option_index);
352 printf("unknown commandline option '%s'\n",argv[optind-1]);
359 rrdcgiArg = rrdcgiInit();
360 server_url = getenv("SERVER_URL");
363 /* make sure we have one extra argument,
364 if there are others, we do not care Apache gives several */
366 /* if ( (optind != argc-2
367 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
368 && optind != argc-1) { */
370 if ( optind >= argc ) {
371 fprintf(stderr, "ERROR: expected a filename\n");
374 length = readfile(argv[optind], &buffer, 1);
377 if(rrd_test_error()) {
378 fprintf(stderr, "ERROR: %s\n",rrd_get_error());
382 /* initialize variable heap */
386 /* some fake header for testing */
387 printf ("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
391 /* expand rrd directives in buffer recursivly */
392 for (i=0; buffer[i]; i++) {
393 if (buffer[i] != '<')
396 parse(&buffer, i, "<RRD::CV", cgiget);
397 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
398 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
399 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
401 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
402 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
403 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
404 parse(&buffer, i, "<RRD::INCLUDE", includefile);
405 parse(&buffer, i, "<RRD::PRINT", drawprint);
406 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
407 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
408 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
409 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
410 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
411 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
415 printf ("Content-Type: text/html\n"
416 "Content-Length: %d\n",
419 if (labs(goodfor) > 0) {
422 printf("Last-Modified: %s\n", http_time(&now));
423 now += labs(goodfor);
424 printf("Expires: %s\n", http_time(&now));
426 printf("Refresh: %ld\n", labs(goodfor));
433 printf("%s", buffer);
444 /* remove occurrences of .. this is a general measure to make
445 paths which came in via cgi do not go UP ... */
447 char* rrdsetenv(long argc, const char **args) {
449 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
451 return stralloc("[ERROR: allocating setenv buffer]");
453 sprintf(xyz, "%s=%s", args[0], args[1]);
454 if(putenv(xyz) == -1) {
456 return stralloc("[ERROR: failed to do putenv]");
460 return stralloc("[ERROR: setenv failed because not enough "
461 "arguments were defined]");
464 /* rrd interface to the variable function putvar() */
466 rrdsetvar(long argc, const char **args)
470 const char* result = putvar(args[0], args[1], 0 /* not const */);
472 /* setvar does not return the value set */
475 return stralloc("[ERROR: putvar failed]");
477 return stralloc("[ERROR: putvar failed because not enough arguments "
481 /* rrd interface to the variable function putvar() */
483 rrdsetvarconst(long argc, const char **args)
487 const char* result = putvar(args[0], args[1], 1 /* const */);
489 /* setvar does not return the value set */
492 return stralloc("[ERROR: putvar failed]");
494 return stralloc("[ERROR: putvar failed because not enough arguments "
498 char* rrdgetenv(long argc, const char **args) {
502 return stralloc("[ERROR: getenv failed because it did not "
503 "get 1 argument only]");
505 envvar = getenv(args[0]);
507 return stralloc(envvar);
509 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
510 _snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
512 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
514 return stralloc(buf);
518 char* rrdgetvar(long argc, const char **args) {
522 return stralloc("[ERROR: getvar failed because it did not "
523 "get 1 argument only]");
525 value = getvar(args[0]);
527 return stralloc(value);
529 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
530 _snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
532 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
534 return stralloc(buf);
538 char* rrdgoodfor(long argc, const char **args){
540 goodfor = atol(args[0]);
542 return stralloc("[ERROR: goodfor expected 1 argument]");
546 return stralloc("[ERROR: goodfor value must not be 0]");
552 /* Format start or end times using strftime. We always need both the
553 * start and end times, because, either might be relative to the other.
555 #define MAX_STRFTIME_SIZE 256
556 char* printstrftime(long argc, const char **args){
557 struct rrd_time_value start_tv, end_tv;
558 char *parsetime_error = NULL;
559 char formatted[MAX_STRFTIME_SIZE];
561 time_t start_tmp, end_tmp;
563 /* Make sure that we were given the right number of args */
565 rrd_set_error( "wrong number of args %d", argc);
569 /* Init start and end time */
570 parsetime("end-24h", &start_tv);
571 parsetime("now", &end_tv);
573 /* Parse the start and end times we were given */
574 if( (parsetime_error = parsetime( args[1], &start_tv))) {
575 rrd_set_error( "start time: %s", parsetime_error);
578 if( (parsetime_error = parsetime( args[2], &end_tv))) {
579 rrd_set_error( "end time: %s", parsetime_error);
582 if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
586 /* Do we do the start or end */
587 if( strcasecmp( args[0], "START") == 0) {
588 the_tm = localtime( &start_tmp);
590 else if( strcasecmp( args[0], "END") == 0) {
591 the_tm = localtime( &end_tmp);
594 rrd_set_error( "start/end not found in '%s'", args[0]);
599 if( strftime( formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
600 return( stralloc( formatted));
603 rrd_set_error( "strftime failed");
608 char* includefile(long argc, const char **args){
611 const char* filename = args[0];
612 readfile(filename, &buffer, 0);
613 if (rrd_test_error()) {
614 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE));
615 sprintf(err, "[ERROR: %s]",rrd_get_error());
624 return stralloc("[ERROR: No Inclue file defined]");
628 /* make a copy of buf and replace open/close brackets with '_' */
629 char* rrdstrip(char *buf) {
634 /* make a copy of the buffer */
642 if (*p == '<' || *p == '>') {
650 char* cgigetq(long argc, const char **args){
652 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
656 if (buf==NULL) return NULL;
658 for(c=buf;*c != '\0';c++)
660 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
661 perror("Malloc Buffer");
682 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
685 /* remove occurrences of .. this is a general measure to make
686 paths which came in via cgi do not go UP ... */
688 char* cgigetqp(long argc, const char **args){
696 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
699 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
705 buf2 = malloc(strlen(buf)+1);
708 perror("cgigetqp(): Malloc Path Buffer");
717 /* prevent mallicious paths from entering the system */
718 if (p[0] == '.' && p[1] == '.')
733 /* Make sure the path is relative, e.g. does not start with '/' */
744 char* cgiget(long argc, const char **args){
746 return rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
748 return stralloc("[ERROR: not enough arguments for RRD::CV]");
753 char* drawgraph(long argc, const char **args){
757 if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
759 args[argc++] = "--imginfo";
760 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
763 if( rrd_graph(argc+1, (char **) args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
764 return stralloc(calcpr[0]);
766 if (rrd_test_error()) {
767 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
768 sprintf(err, "[ERROR: %s]",rrd_get_error());
777 char* drawprint(long argc, const char **args){
778 if (argc==1 && calcpr){
780 while (calcpr[i] != NULL) i++; /*determine number lines in calcpr*/
781 if (atol(args[0])<i-1)
782 return stralloc(calcpr[atol(args[0])+1]);
784 return stralloc("[ERROR: RRD::PRINT argument error]");
787 char* printtimelast(long argc, const char **args) {
794 return stralloc("[ERROR: allocating strftime buffer]");
796 last = rrd_last(argc+1, (char **) args-1);
797 if (rrd_test_error()) {
798 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
799 sprintf(err, "[ERROR: %s]",rrd_get_error());
803 tm_last = *localtime(&last);
804 strftime(buf,254,args[1],&tm_last);
808 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
810 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
813 char* printtimenow(long argc, const char **args) {
814 time_t now = time(NULL);
820 return stralloc("[ERROR: allocating strftime buffer]");
822 tm_now = *localtime(&now);
823 strftime(buf,254,args[0],&tm_now);
827 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
829 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
832 /* Scan buffer until an unescaped '>' arives.
833 * Update argument array with arguments found.
834 * Return end cursor where parsing stopped, or NULL in case of failure.
837 * To allow nested constructs, we call rrd_expand_vars() for arguments
838 * that contain RRD::x directives. These introduce a small memory leak
839 * since we have to stralloc the arguments the way parse() works.
842 scanargs(char *line, int *argument_count, char ***arguments)
844 char *getP; /* read cursor */
845 char *putP; /* write cursor */
846 char Quote; /* type of quote if in quoted string, 0 otherwise */
847 int tagcount; /* open tag count */
848 int in_arg; /* if we currently are parsing an argument or not */
849 int argsz; /* argument array size */
850 int curarg_contains_rrd_directives;
852 /* local array of arguments while parsing */
857 printf("<-- scanargs(%s) -->\n", line);
863 /* create initial argument array of char pointers */
865 argv = (char **)malloc(argsz * sizeof(char *));
870 /* skip leading blanks */
871 while (isspace((int)*line)) {
882 curarg_contains_rrd_directives = 0;
884 /* start parsing 'line' for arguments */
887 unsigned char c = *getP++;
889 if (c == '>' && !Quote && !tagcount) {
890 /* this is our closing tag, quit scanning */
894 /* remove all special chars */
902 if (Quote || tagcount) {
903 /* copy quoted/tagged (=RRD expanded) string */
908 /* end argument string */
911 if (curarg_contains_rrd_directives) {
912 argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
913 curarg_contains_rrd_directives = 0;
918 case '"': /* Fall through */
924 /* copy quoted string */
929 /* reference start of argument string in argument array */
939 /* start new argument */
950 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
951 curarg_contains_rrd_directives = 1;
958 /* check if our argument array is still large enough */
960 /* resize argument array */
962 argv = rrd_realloc(argv, argsz * sizeof(char *));
969 /* terminate last argument found */
971 if (curarg_contains_rrd_directives) {
972 argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
978 printf("<-- arguments found [%d]\n", argc);
979 for (n=0; n<argc; n++) {
980 printf("arg %02d: '%s'\n", n, argv[n]);
984 printf("<!-- No arguments found -->\n");
988 /* update caller's notion of the argument array and it's size */
990 *argument_count = argc;
996 /* Return new scanning cursor:
997 pointer to char after closing bracket */
1003 * Parse(): scan current portion of buffer for given tag.
1004 * If found, parse tag arguments and call 'func' for it.
1005 * The result of func is inserted at the current position
1010 char **buf, /* buffer */
1011 long i, /* offset in buffer */
1012 char *tag, /* tag to handle */
1013 char *(*func)(long , const char **) /* function to call for 'tag' */
1016 /* the name of the vairable ... */
1023 size_t taglen = strlen(tag);
1025 /* Current position in buffer should start with 'tag' */
1026 if (strncmp((*buf)+i, tag, taglen) != 0) {
1029 /* .. and match exactly (a whitespace following 'tag') */
1030 if (! isspace(*((*buf) + i + taglen)) ) {
1035 printf("parse(): handling tag '%s'\n", tag);
1038 /* Scan for arguments following the tag;
1039 scanargs() puts \0 into *buf ... so after scanargs it is probably
1040 not a good time to use strlen on buf */
1041 end = scanargs((*buf) + i + taglen, &argc, &args);
1044 /* got arguments, call function for 'tag' with arguments */
1045 val = func(argc, (const char **) args);
1050 /* unable to parse arguments, undo 0-termination by scanargs */
1051 for (; argc > 0; argc--) {
1052 *((args[argc-1])-1) = ' ';
1055 /* next call, try parsing at current offset +1 */
1056 end = (*buf) + i + 1;
1058 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1059 " Check original file. This may have been altered "
1060 "by parsing.]\n\n");
1063 /* remember offset where we have to continue parsing */
1064 end_offset = end - (*buf);
1068 valln = strlen(val);
1071 /* Optionally resize buffer to hold the replacement value:
1072 Calculating the new length of the buffer is simple. add current
1073 buffer pos (i) to length of string after replaced tag to length
1074 of replacement string and add 1 for the final zero ... */
1075 if (end - (*buf) < (i + valln)) {
1076 /* make sure we do not shrink the mallocd block */
1077 size_t newbufsize = i + strlen(end) + valln + 1;
1078 *buf = rrd_realloc(*buf, newbufsize);
1081 perror("Realoc buf:");
1086 /* Update new end pointer:
1087 make sure the 'end' pointer gets moved along with the
1088 buf pointer when realloc moves memory ... */
1089 end = (*buf) + end_offset;
1091 /* splice the variable:
1092 step 1. Shift pending data to make room for 'val' */
1093 memmove((*buf) + i + valln, end, strlen(end) + 1);
1095 /* step 2. Insert val */
1097 memmove((*buf)+i, val, valln);
1100 return (valln > 0 ? valln-1: valln);
1104 http_time(time_t *now) {
1106 static char buf[60];
1108 tmptime=gmtime(now);
1109 strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
1113 void rrdcgiHeader(void)
1116 printf ("Content-type: %s\n", rrdcgiType);
1118 printf ("Content-type: text/html\n");
1119 if (rrdcgiHeaderString)
1120 printf ("%s", rrdcgiHeaderString);
1124 void rrdcgiDebug(int level, int where)
1127 rrdcgiDebugLevel = level;
1129 rrdcgiDebugLevel = 0;
1131 rrdcgiDebugStderr = 0;
1133 rrdcgiDebugStderr = 1;
1136 char *rrdcgiDecodeString(char *text)
1140 for (cp=text,xp=text; *cp; cp++) {
1142 if (strchr("0123456789ABCDEFabcdef", *(cp+1))
1143 && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
1144 if (islower(*(cp+1)))
1145 *(cp+1) = toupper(*(cp+1));
1146 if (islower(*(cp+2)))
1147 *(cp+2) = toupper(*(cp+2));
1148 *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
1149 + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
1156 memset(xp, 0, cp-xp);
1160 /* rrdcgiReadVariables()
1162 * Read from stdin if no string is provided via CGI. Variables that
1163 * doesn't have a value associated with it doesn't get stored.
1165 s_var **rrdcgiReadVariables(void)
1170 char *cp, *ip, *esp, *sptr;
1175 cp = getenv("REQUEST_METHOD");
1176 ip = getenv("CONTENT_LENGTH");
1178 if (cp && !strcmp(cp, "POST")) {
1181 if ((line = (char *)malloc (length+2)) == NULL)
1183 fgets(line, length+1, stdin);
1186 } else if (cp && !strcmp(cp, "GET")) {
1187 esp = getenv("QUERY_STRING");
1188 if (esp && strlen(esp)) {
1189 if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
1191 sprintf (line, "%s", esp);
1196 printf ("(offline mode: enter name=value pairs on standard input)\n");
1197 memset (tmp, 0, sizeof(tmp));
1198 while((cp = fgets (tmp, 100, stdin)) != NULL) {
1200 if (tmp[strlen(tmp)-1] == '\n')
1201 tmp[strlen(tmp)-1] = '&';
1203 length += strlen(tmp);
1204 len = (length+1) * sizeof(char);
1205 if ((line = (char *)realloc (line, len)) == NULL)
1209 length = strlen(tmp);
1210 len = (length+1) * sizeof(char);
1211 if ((line = (char *)malloc (len)) == NULL)
1213 memset (line, 0, len);
1217 memset (tmp, 0, sizeof(tmp));
1221 if (line[strlen(line)-1] == '&')
1222 line[strlen(line)-1] = '\0';
1226 * From now on all cgi variables are stored in the variable line
1227 * and look like foo=bar&foobar=barfoo&foofoo=
1230 if (rrdcgiDebugLevel > 0) {
1231 if (rrdcgiDebugStderr)
1232 fprintf (stderr, "Received cgi input: %s\n", line);
1234 printf ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n", line);
1237 for (cp=line; *cp; cp++)
1242 for (numargs=1,cp=line; *cp; cp++)
1243 if (*cp == '&') numargs++;
1246 if (rrdcgiDebugLevel > 0) {
1247 if (rrdcgiDebugStderr)
1248 fprintf (stderr, "%d cgi variables found.\n", numargs);
1250 printf ("%d cgi variables found.<br>\n", numargs);
1253 len = (numargs+1) * sizeof(s_var *);
1254 if ((result = (s_var **)malloc (len)) == NULL)
1256 memset (result, 0, len);
1261 if ((ip = (char *)strchr(cp, '&')) != NULL) {
1264 ip = cp + strlen(cp);
1266 if ((esp=(char *)strchr(cp, '=')) == NULL) {
1278 /* try to find out if there's already such a variable */
1279 for (k=0; k<i && (strncmp (result[k]->name,cp, esp-cp) || !(strlen (result[k]->name) == esp-cp)); k++);
1281 if (k == i) { /* No such variable yet */
1282 if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
1284 if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
1286 memset (result[i]->name, 0, esp-cp+1);
1287 strncpy(result[i]->name, cp, esp-cp);
1289 if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
1291 memset (result[i]->value, 0, ip-esp+1);
1292 strncpy(result[i]->value, cp, ip-esp);
1293 result[i]->value = rrdcgiDecodeString(result[i]->value);
1294 if (rrdcgiDebugLevel) {
1295 if (rrdcgiDebugStderr)
1296 fprintf (stderr, "%s: %s\n", result[i]->name, result[i]->value);
1298 printf ("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n", result[i]->name, result[i]->value);
1301 } else { /* There is already such a name, suppose a mutiple field */
1303 len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
1304 if ((sptr = (char *)malloc(len)) == NULL)
1306 memset (sptr, 0, len);
1307 sprintf (sptr, "%s\n", result[k]->value);
1308 strncat(sptr, cp, ip-esp);
1309 free(result[k]->value);
1310 result[k]->value = rrdcgiDecodeString (sptr);
1320 * Read from stdin if no string is provided via CGI. Variables that
1321 * doesn't have a value associated with it doesn't get stored.
1323 s_cgi *rrdcgiInit(void)
1328 vars = rrdcgiReadVariables();
1333 if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
1340 char *rrdcgiGetValue(s_cgi *parms, const char *name)
1344 if (!parms || !parms->vars)
1346 for (i=0;parms->vars[i]; i++)
1347 if (!strcmp(name,parms->vars[i]->name)) {
1348 if (rrdcgiDebugLevel > 0) {
1349 if (rrdcgiDebugStderr)
1350 fprintf (stderr, "%s found as %s\n", name, parms->vars[i]->value);
1352 printf ("%s found as %s<br>\n", name, parms->vars[i]->value);
1354 return parms->vars[i]->value;
1356 if (rrdcgiDebugLevel) {
1357 if (rrdcgiDebugStderr)
1358 fprintf (stderr, "%s not found\n", name);
1360 printf ("%s not found<br>\n", name);
1365 void rrdcgiFreeList (char **list)
1369 for (i=0; list[i] != NULL; i++)
1374 void rrdcgiFree (s_cgi *parms)
1381 for (i=0;parms->vars[i]; i++) {
1382 if (parms->vars[i]->name)
1383 free (parms->vars[i]->name);
1384 if (parms->vars[i]->value)
1385 free (parms->vars[i]->value);
1386 free (parms->vars[i]);
1392 if (rrdcgiHeaderString) {
1393 free (rrdcgiHeaderString);
1394 rrdcgiHeaderString = NULL;