1 /*****************************************************************************
2  * RRDtool 1.2.30  Copyright by Tobi Oetiker, 1997-2009
3  *****************************************************************************
4  * rrd_cgi.c  RRD Web Page Generator
5  *****************************************************************************/
6 
7 #include "rrd_tool.h"
8 
9 
10 #define MEMBLK 1024
11 /*#define DEBUG_PARSER
12 #define DEBUG_VARS*/
13 
14 typedef struct var_s {
15 	char	*name, *value;
16 } s_var;
17 
18 typedef struct cgi_s {
19 	s_var **vars;
20 } s_cgi;
21 
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 **));
25 
26 /**************************************************/
27 /* tag replacers ... they are called from parse   */
28 /* through function pointers                      */
29 /**************************************************/
30 
31 /* return cgi var named arg[0] */
32 char* cgiget(long , const char **);
33 
34 /* return a quoted cgi var named arg[0] */
35 char* cgigetq(long , const char **);
36 
37 /* return a quoted and sanitized cgi variable */
38 char* cgigetqp(long , const char **);
39 
40 /* call rrd_graph and insert appropriate image tag */
41 char* drawgraph(long, const char **);
42 
43 /* return PRINT functions from last rrd_graph call */
44 char* drawprint(long, const char **);
45 
46 /* pretty-print the <last></last> value for some.rrd via strftime() */
47 char* printtimelast(long, const char **);
48 
49 /* pretty-print current time */
50 char* printtimenow(long, const char **);
51 
52 /* set an environment variable */
53 char* rrdsetenv(long, const char **);
54 
55 /* get an environment variable */
56 char* rrdgetenv(long, const char **);
57 
58 /* include the named file at this point */
59 char* includefile(long, const char **);
60 
61 /* for how long is the output of the cgi valid ? */
62 char* rrdgoodfor(long, const char **);
63 
64 /* return rrdcgi version string */
65 char* rrdgetinternal(long, const char **);
66 
67 char* rrdstrip(char *buf);
68 char* scanargs(char *line, int *argc, char ***args);
69 
70 /* format at-time specified times using strftime */
71 char* printstrftime(long, const char**);
72 
73 /** HTTP protocol needs special format, and GMT time **/
74 char *http_time(time_t *);
75 
76 /* return a pointer to newly allocated copy of this string */
77 char *stralloc(const char *);
78 
79 /* global variable for rrdcgi */
80 s_cgi *rrdcgiArg;
81 
82 /* rrdcgiHeader
83  *
84  *  Prints a valid CGI Header (Content-type...) etc.
85  */
86 void rrdcgiHeader(void);
87 
88 /* rrdcgiDecodeString
89  * decode html escapes
90  */
91 
92 char *rrdcgiDecodeString(char *text);
93 
94 /* rrdcgiDebug
95  *
96  *  Set/unsets debugging
97  */
98 void rrdcgiDebug(int level, int where);
99 
100 /* rrdcgiInit
101  *
102  *  Reads in variables set via POST or stdin.
103  */
104 s_cgi *rrdcgiInit (void);
105 
106 /* rrdcgiGetValue
107  *
108  *  Returns the value of the specified variable or NULL if it's empty
109  *  or doesn't exist.
110  */
111 char *rrdcgiGetValue (s_cgi *parms, const char *name);
112 
113 /* rrdcgiFreeList
114  *
115  * Frees a list as returned by rrdcgiGetVariables()
116  */
117 void rrdcgiFreeList (char **list);
118 
119 /* rrdcgiFree
120  *
121  * Frees the internal data structures
122  */
123 void rrdcgiFree (s_cgi *parms);
124 
125 /*  rrdcgiReadVariables()
126  *
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.
129  */
130 s_var **rrdcgiReadVariables(void);
131 
132 
133 int rrdcgiDebugLevel = 0;
134 int rrdcgiDebugStderr = 1;
135 char *rrdcgiHeaderString = NULL;
136 char *rrdcgiType = NULL;
137 
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);
142 
143 
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);
149 
150 /* key value pair that makes up an entry in the variable store */
151 typedef struct
152 {
153 	int is_const;		/* const variable or not */
154 	const char* name;	/* variable name */
155 	const char* value;	/* variable value */
156 } vardata;
157 
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;
163 
164 /* allocate and initialize variable heap */
165 static int
initvar()166 initvar()
167 {
168 	varheap = (vardata*)malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
169 	if (varheap == NULL) {
170 		fprintf(stderr, "ERROR: unable to initialize variable store\n");
171 		return -1;
172 	}
173 	memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
174 	varheap_size = INIT_VARSTORE_SIZE;
175 	return 0;
176 }
177 
178 /* cleanup: free allocated memory */
179 static void
donevar()180 donevar()
181 {
182 	int i;
183 	if (varheap) {
184 		for (i=0; i<(int)varheap_size; i++) {
185 			if (varheap[i].name) {
186 				free((char*)varheap[i].name);
187 			}
188 			if (varheap[i].value) {
189 				free((char*)varheap[i].value);
190 			}
191 		}
192 		free(varheap);
193 	}
194 }
195 
196 /* Get a variable from the variable store.
197    Return NULL in case the requested variable was not found. */
198 static const char*
getvar(const char * name)199 getvar(const char* name)
200 {
201 	int i;
202 	for (i=0; i<(int)varheap_size && varheap[i].name; i++) {
203 		if (0 == strcmp(name, varheap[i].name)) {
204 #ifdef		DEBUG_VARS
205 			printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
206 #endif
207 			return varheap[i].value;
208 		}
209 	}
210 #ifdef DEBUG_VARS
211 	printf("<!-- getvar(%s) -> Not found-->\n", name);
212 #endif
213 	return NULL;
214 }
215 
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. */
220 static const char*
putvar(const char * name,const char * value,int is_const)221 putvar(const char* name, const char* value, int is_const)
222 {
223 	int i;
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) {
228 #ifdef			DEBUG_VARS
229 				printf("<!-- setver(%s, %s): not assigning: "
230 						"const variable -->\n", name, value);
231 #				endif
232 				return varheap[i].value;
233 			}
234 #ifdef		DEBUG_VARS
235 			printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
236 					name, value, varheap[i].value);
237 #endif
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;
243 		}
244 	}
245 
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));
251 		if (!varheap) {
252 			fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
253 			return NULL;
254 		}
255 		/* initialize newly allocated memory */;
256 		memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
257 		varheap_size = new_size;
258 	}
259 	varheap[i].is_const = is_const;
260 	varheap[i].name  = stralloc(name);
261 	varheap[i].value = stralloc(value);
262 
263 #ifdef		DEBUG_VARS
264 	printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
265 #endif
266 	return varheap[i].value;
267 }
268 
269 /* expand those RRD:* directives that can be used recursivly */
270 static char*
rrd_expand_vars(char * buffer)271 rrd_expand_vars(char* buffer)
272 {
273 	int i;
274 
275 #ifdef DEBUG_PARSER
276 	printf("expanding variables in '%s'\n", buffer);
277 #endif
278 
279 	for (i=0; buffer[i]; i++) {
280 		if (buffer[i] != '<')
281 			continue;
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);
291 	}
292 	return buffer;
293 }
294 
295 static long goodfor=0;
296 static char **calcpr=NULL;
calfree(void)297 static void calfree (void){
298   if (calcpr) {
299     long i;
300     for(i=0;calcpr[i];i++){
301       if (calcpr[i]){
302 	      free(calcpr[i]);
303       }
304     }
305     if (calcpr) {
306 	    free(calcpr);
307     }
308     calcpr=NULL;
309   }
310 }
311 
312 /* create freeable version of the string */
stralloc(const char * str)313 char * stralloc(const char *str){
314   char* nstr;
315   if (!str) {
316 	  return NULL;
317   }
318   nstr = malloc((strlen(str)+1));
319   strcpy(nstr,str);
320   return(nstr);
321 }
322 
main(int argc,char * argv[])323 int main(int argc, char *argv[]) {
324 	long length;
325 	char *buffer;
326 	char *server_url = NULL;
327 	long i;
328 	long filter=0;
329 #ifdef MUST_DISABLE_SIGFPE
330 	signal(SIGFPE,SIG_IGN);
331 #endif
332 #ifdef MUST_DISABLE_FPMASK
333 	fpsetmask(0);
334 #endif
335         optind = 0; opterr = 0;  /* initialize getopt */
336 
337 	/* what do we get for cmdline arguments?
338 	for (i=0;i<argc;i++)
339 	printf("%d-'%s'\n",i,argv[i]); */
340 	while (1) {
341 		static struct option long_options[] = {
342 			{ "filter", no_argument, 0, 'f' },
343 			{ 0, 0, 0, 0}
344 		};
345 		int option_index = 0;
346 		int opt;
347 		opt = getopt_long(argc, argv, "f", long_options, &option_index);
348 		if (opt == EOF) {
349 			break;
350 		}
351 
352 		switch(opt) {
353 		case 'f':
354 				filter=1;
355 			break;
356 		case '?':
357 			printf("unknown commandline option '%s'\n",argv[optind-1]);
358 			return -1;
359 		}
360 	}
361 
362 	if (!filter) {
363 		rrdcgiDebug(0,0);
364 		rrdcgiArg = rrdcgiInit();
365 		server_url = getenv("SERVER_URL");
366 	}
367 
368 	/* make sure we have one extra argument,
369 	   if there are others, we do not care Apache gives several */
370 
371 	/* if ( (optind != argc-2
372 	   && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
373 	   && optind != argc-1) { */
374 
375 	if ( optind >= argc ) {
376 		fprintf(stderr, "ERROR: expected a filename\n");
377 		exit(1);
378 	} else {
379 		length = readfile(argv[optind], &buffer, 1);
380 	}
381 
382 	if(rrd_test_error()) {
383 		fprintf(stderr, "ERROR: %s\n",rrd_get_error());
384 		exit(1);
385 	}
386 
387 	/* initialize variable heap */
388 	initvar();
389 
390 #ifdef DEBUG_PARSER
391        /* some fake header for testing */
392        printf ("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
393 #endif
394 
395 
396 	/* expand rrd directives in buffer recursivly */
397 	for (i=0; buffer[i]; i++) {
398 		if (buffer[i] != '<')
399 			continue;
400 		if (!filter) {
401 			parse(&buffer, i, "<RRD::CV", cgiget);
402 			parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
403 			parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
404 			parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
405 		}
406 		parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
407 		parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
408 		parse(&buffer, i, "<RRD::GRAPH", drawgraph);
409 		parse(&buffer, i, "<RRD::INCLUDE", includefile);
410 		parse(&buffer, i, "<RRD::PRINT", drawprint);
411 		parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
412 		parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
413 		parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
414 		parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
415 		parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
416 		parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
417 		parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
418 	}
419 
420 	if (!filter) {
421 		printf ("Content-Type: text/html\n"
422 				"Content-Length: %d\n",
423 				strlen(buffer));
424 
425 		if (labs(goodfor) > 0) {
426 			time_t now;
427 			now = time(NULL);
428 			printf("Last-Modified: %s\n", http_time(&now));
429 			now += labs(goodfor);
430 			printf("Expires: %s\n", http_time(&now));
431 			if (goodfor < 0) {
432 				printf("Refresh: %ld\n", labs(goodfor));
433 			}
434 		}
435 		printf("\n");
436 	}
437 
438 	/* output result */
439 	printf("%s", buffer);
440 
441 	/* cleanup */
442 	calfree();
443 	if (buffer){
444 		free(buffer);
445 	}
446 	donevar();
447 	exit(0);
448 }
449 
450 /* remove occurrences of .. this is a general measure to make
451    paths which came in via cgi do not go UP ... */
452 
rrdsetenv(long argc,const char ** args)453 char* rrdsetenv(long argc, const char **args) {
454 	if (argc >= 2) {
455 		char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
456 		if (xyz == NULL) {
457 			return stralloc("[ERROR: allocating setenv buffer]");
458 		};
459 		sprintf(xyz, "%s=%s", args[0], args[1]);
460 		if(putenv(xyz) == -1) {
461 			free(xyz);
462 			return stralloc("[ERROR: failed to do putenv]");
463 		};
464  	        return stralloc("");
465 	}
466 	return stralloc("[ERROR: setenv failed because not enough "
467 		  			"arguments were defined]");
468 }
469 
470 /* rrd interface to the variable function putvar() */
471 char*
rrdsetvar(long argc,const char ** args)472 rrdsetvar(long argc, const char **args)
473 {
474 	if (argc >= 2)
475 	{
476 		const char* result = putvar(args[0], args[1], 0 /* not const */);
477 		if (result) {
478 			/* setvar does not return the value set */
479 			return stralloc("");
480 		}
481 		return stralloc("[ERROR: putvar failed]");
482 	}
483 	return stralloc("[ERROR: putvar failed because not enough arguments "
484 					"were defined]");
485 }
486 
487 /* rrd interface to the variable function putvar() */
488 char*
rrdsetvarconst(long argc,const char ** args)489 rrdsetvarconst(long argc, const char **args)
490 {
491 	if (argc >= 2)
492 	{
493 		const char* result = putvar(args[0], args[1], 1 /* const */);
494 		if (result) {
495 			/* setvar does not return the value set */
496 			return stralloc("");
497 		}
498 		return stralloc("[ERROR: putvar failed]");
499 	}
500 	return stralloc("[ERROR: putvar failed because not enough arguments "
501 					"were defined]");
502 }
503 
rrdgetenv(long argc,const char ** args)504 char* rrdgetenv(long argc, const char **args) {
505 	char buf[128];
506 	const char* envvar;
507 	if (argc != 1) {
508 		return stralloc("[ERROR: getenv failed because it did not "
509 						"get 1 argument only]");
510 	};
511 	envvar = getenv(args[0]);
512 	if (envvar) {
513 		return stralloc(envvar);
514 	} else {
515                 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
516                 return stralloc(buf);
517 	}
518 }
519 
rrdgetvar(long argc,const char ** args)520 char* rrdgetvar(long argc, const char **args) {
521 	char buf[128];
522 	const char* value;
523 	if (argc != 1) {
524 		return stralloc("[ERROR: getvar failed because it did not "
525 						"get 1 argument only]");
526 	};
527 	value = getvar(args[0]);
528 	if (value) {
529 		return stralloc(value);
530 	} else {
531                 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
532 		return stralloc(buf);
533 	}
534 }
535 
rrdgoodfor(long argc,const char ** args)536 char* rrdgoodfor(long argc, const char **args){
537   if (argc == 1) {
538       goodfor = atol(args[0]);
539   } else {
540     return stralloc("[ERROR: goodfor expected 1 argument]");
541   }
542 
543   if (goodfor == 0){
544      return stralloc("[ERROR: goodfor value must not be 0]");
545   }
546 
547   return stralloc("");
548 }
549 
rrdgetinternal(long argc,const char ** args)550 char* rrdgetinternal(long argc, const char **args){
551   if (argc == 1) {
552     if( strcasecmp( args[0], "VERSION") == 0) {
553       return stralloc(PACKAGE_VERSION);
554     } else if( strcasecmp( args[0], "COMPILETIME") == 0) {
555       return stralloc(__DATE__ " " __TIME__);
556     } else {
557       return stralloc("[ERROR: internal unknown argument]");
558     }
559   } else {
560     return stralloc("[ERROR: internal expected 1 argument]");
561   }
562 }
563 
564 /* Format start or end times using strftime.  We always need both the
565  * start and end times, because, either might be relative to the other.
566  * */
567 #define MAX_STRFTIME_SIZE 256
printstrftime(long argc,const char ** args)568 char* printstrftime(long argc, const char **args){
569 	struct	rrd_time_value start_tv, end_tv;
570 	char    *parsetime_error = NULL;
571 	char	formatted[MAX_STRFTIME_SIZE];
572 	struct tm *the_tm;
573 	time_t	start_tmp, end_tmp;
574 
575 	/* Make sure that we were given the right number of args */
576 	if( argc != 4) {
577 		rrd_set_error( "wrong number of args %d", argc);
578 		return stralloc("");
579 	}
580 
581 	/* Init start and end time */
582 	parsetime("end-24h", &start_tv);
583 	parsetime("now", &end_tv);
584 
585 	/* Parse the start and end times we were given */
586 	if( (parsetime_error = parsetime( args[1], &start_tv))) {
587 		rrd_set_error( "start time: %s", parsetime_error);
588 		return stralloc("");
589 	}
590 	if( (parsetime_error = parsetime( args[2], &end_tv))) {
591 		rrd_set_error( "end time: %s", parsetime_error);
592 		return stralloc("");
593 	}
594 	if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
595 		return stralloc("");
596 	}
597 
598 	/* Do we do the start or end */
599 	if( strcasecmp( args[0], "START") == 0) {
600 		the_tm = localtime( &start_tmp);
601 	}
602 	else if( strcasecmp( args[0], "END") == 0) {
603 		the_tm = localtime( &end_tmp);
604 	}
605 	else {
606 		rrd_set_error( "start/end not found in '%s'", args[0]);
607 		return stralloc("");
608 	}
609 
610 	/* now format it */
611 	if( strftime( formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
612 		return( stralloc( formatted));
613 	}
614 	else {
615 		rrd_set_error( "strftime failed");
616 		return stralloc("");
617 	}
618 }
619 
includefile(long argc,const char ** args)620 char* includefile(long argc, const char **args){
621   char *buffer;
622   if (argc >= 1) {
623       const char* filename = args[0];
624       readfile(filename, &buffer, 0);
625       if (rrd_test_error()) {
626 	  	char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE));
627 	  sprintf(err, "[ERROR: %s]",rrd_get_error());
628 	  rrd_clear_error();
629 	  return err;
630       } else {
631        return buffer;
632       }
633   }
634   else
635   {
636       return stralloc("[ERROR: No Inclue file defined]");
637   }
638 }
639 
640 /* make a copy of buf and replace open/close brackets with '_' */
rrdstrip(char * buf)641 char* rrdstrip(char *buf) {
642   char* p;
643   if (buf == NULL) {
644 	  return NULL;
645   }
646   /* make a copy of the buffer */
647   buf = stralloc(buf);
648   if (buf == NULL) {
649 	  return NULL;
650   }
651 
652   p = buf;
653   while (*p) {
654 	  if (*p == '<' || *p == '>') {
655 		  *p = '_';
656 	  }
657 	  p++;
658   }
659   return buf;
660 }
661 
cgigetq(long argc,const char ** args)662 char* cgigetq(long argc, const char **args){
663   if (argc>= 1){
664     char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
665     char *buf2;
666     char *c,*d;
667     int  qc=0;
668     if (buf==NULL) return NULL;
669 
670     for(c=buf;*c != '\0';c++)
671       if (*c == '"') qc++;
672     if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
673 	perror("Malloc Buffer");
674 	exit(1);
675     };
676     c=buf;
677     d=buf2;
678     *(d++) = '"';
679     while(*c != '\0'){
680 	if (*c == '"') {
681 	    *(d++) = '"';
682 	    *(d++) = '\'';
683 	    *(d++) = '"';
684 	    *(d++) = '\'';
685 	}
686 	*(d++) = *(c++);
687     }
688     *(d++) = '"';
689     *(d) = '\0';
690     free(buf);
691     return buf2;
692   }
693 
694   return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
695 }
696 
697 /* remove occurrences of .. this is a general measure to make
698    paths which came in via cgi do not go UP ... */
699 
cgigetqp(long argc,const char ** args)700 char* cgigetqp(long argc, const char **args){
701        char* buf;
702     char* buf2;
703     char* p;
704         char* d;
705 
706         if (argc < 1)
707         {
708                 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
709         }
710 
711         buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
712     if (!buf)
713         {
714                 return NULL;
715         }
716 
717         buf2 = malloc(strlen(buf)+1);
718     if (!buf2)
719         {
720                 perror("cgigetqp(): Malloc Path Buffer");
721                 exit(1);
722     };
723 
724     p = buf;
725     d = buf2;
726 
727     while (*p)
728         {
729                 /* prevent mallicious paths from entering the system */
730                 if (p[0] == '.' && p[1] == '.')
731                 {
732                         p += 2;
733                         *d++ = '_';
734                         *d++ = '_';
735                 }
736                 else
737                 {
738                         *d++ = *p++;
739                 }
740     }
741 
742     *d = 0;
743     free(buf);
744 
745     /* Make sure the path is relative, e.g. does not start with '/' */
746     p = buf2;
747     while ('/' == *p)
748         {
749             *p++ = '_';
750     }
751 
752     return buf2;
753 }
754 
755 
cgiget(long argc,const char ** args)756 char* cgiget(long argc, const char **args){
757   if (argc>= 1)
758     return rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
759   else
760     return stralloc("[ERROR: not enough arguments for RRD::CV]");
761 }
762 
763 
764 
drawgraph(long argc,const char ** args)765 char* drawgraph(long argc, const char **args){
766   int i,xsize, ysize;
767   double ymin,ymax;
768   for(i=0;i<argc;i++)
769     if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
770   if(i==argc) {
771     args[argc++] = "--imginfo";
772     args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
773   }
774   calfree();
775   if( rrd_graph(argc+1, (char **) args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
776     return stralloc(calcpr[0]);
777   } else {
778     if (rrd_test_error()) {
779       char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
780       sprintf(err, "[ERROR: %s]",rrd_get_error());
781       rrd_clear_error();
782       return err;
783     }
784   }
785   return NULL;
786 }
787 
drawprint(long argc,const char ** args)788 char* drawprint(long argc, const char **args){
789   if (argc==1 && calcpr){
790     long i=0;
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]);
794   }
795   return stralloc("[ERROR: RRD::PRINT argument error]");
796 }
797 
printtimelast(long argc,const char ** args)798 char* printtimelast(long argc, const char **args) {
799   time_t last;
800   struct tm tm_last;
801   char *buf;
802   if ( argc == 2 ) {
803     buf = malloc(255);
804     if (buf == NULL){
805 	return stralloc("[ERROR: allocating strftime buffer]");
806     };
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());
811       rrd_clear_error();
812       return err;
813     }
814     tm_last = *localtime(&last);
815     strftime(buf,254,args[1],&tm_last);
816     return buf;
817   }
818   if ( argc < 2 ) {
819     return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
820   }
821   return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
822 }
823 
printtimenow(long argc,const char ** args)824 char* printtimenow(long argc, const char **args) {
825   time_t now = time(NULL);
826   struct tm tm_now;
827   char *buf;
828   if ( argc == 1 ) {
829     buf = malloc(255);
830     if (buf == NULL){
831 	return stralloc("[ERROR: allocating strftime buffer]");
832     };
833     tm_now = *localtime(&now);
834     strftime(buf,254,args[0],&tm_now);
835     return buf;
836   }
837   if ( argc < 1 ) {
838     return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
839   }
840   return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
841 }
842 
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.
846  *
847  * FIXME:
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.
851  */
852 char*
scanargs(char * line,int * argument_count,char *** arguments)853 scanargs(char *line, int *argument_count, char ***arguments)
854 {
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;
862 
863 	/* local array of arguments while parsing */
864 	int argc = 0;
865 	char** argv;
866 
867 #ifdef DEBUG_PARSER
868 	printf("<-- scanargs(%s) -->\n", line);
869 #endif
870 
871 	*arguments = NULL;
872 	*argument_count = 0;
873 
874 	/* create initial argument array of char pointers */
875 	argsz = 32;
876 	argv = (char **)malloc(argsz * sizeof(char *));
877 	if (!argv) {
878 		return NULL;
879 	}
880 
881 	/* skip leading blanks */
882 	while (isspace((int)*line)) {
883 		line++;
884 	}
885 
886 	getP = line;
887 	putP = line;
888 
889 	Quote    = 0;
890 	in_arg   = 0;
891 	tagcount = 0;
892 
893 	curarg_contains_rrd_directives = 0;
894 
895 	/* start parsing 'line' for arguments */
896 	while (*getP)
897 	{
898 		unsigned char c = *getP++;
899 
900 		if (c == '>' && !Quote && !tagcount) {
901 			/* this is our closing tag, quit scanning */
902 			break;
903 		}
904 
905  		/* remove all special chars */
906 		if (c < ' ') {
907 			c = ' ';
908 		}
909 
910 		switch (c)
911 		{
912 		case ' ':
913 			if (Quote || tagcount) {
914 				/* copy quoted/tagged (=RRD expanded) string */
915 				*putP++ = c;
916 			}
917 			else if (in_arg)
918 			{
919 				/* end argument string */
920 				*putP++ = 0;
921 				in_arg = 0;
922 				if (curarg_contains_rrd_directives) {
923 					argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
924 					curarg_contains_rrd_directives = 0;
925 				}
926 			}
927 			break;
928 
929 		case '"': /* Fall through */
930 		case '\'':
931 			if (Quote != 0) {
932 				if (Quote == c) {
933 			  		Quote = 0;
934 				} else {
935 					/* copy quoted string */
936 					*putP++ = c;
937 				}
938 			} else {
939 				if (!in_arg) {
940 					/* reference start of argument string in argument array */
941 					argv[argc++] = putP;
942 					in_arg=1;
943 				}
944 				Quote = c;
945 			}
946 			break;
947 
948 		default:
949 				if (!in_arg) {
950 					/* start new argument */
951 					argv[argc++] = putP;
952 					in_arg = 1;
953 				}
954 				if (c == '>') {
955 					if (tagcount) {
956 						tagcount--;
957 					}
958 				}
959 				if (c == '<') {
960 					tagcount++;
961 					if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
962 						curarg_contains_rrd_directives = 1;
963 					}
964 				}
965 			*putP++ = c;
966 			break;
967 		}
968 
969 		/* check if our argument array is still large enough */
970 		if (argc == argsz) {
971 			/* resize argument array */
972 			argsz *= 2;
973 			argv = rrd_realloc(argv, argsz * sizeof(char *));
974 			if (*argv == NULL) {
975 				return NULL;
976 			}
977 		}
978 	}
979 
980 	/* terminate last argument found */
981 	*putP = '\0';
982 	if (curarg_contains_rrd_directives) {
983 		argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
984 	}
985 
986 #ifdef DEBUG_PARSER
987 	if (argc > 0) {
988 		int n;
989 		printf("<-- arguments found [%d]\n", argc);
990 		for (n=0; n<argc; n++) {
991 			printf("arg %02d: '%s'\n", n, argv[n]);
992 		}
993 		printf("-->\n");
994 	} else {
995 		printf("<!-- No arguments found -->\n");
996 	}
997 #endif
998 
999 	/* update caller's notion of the argument array and it's size */
1000 	*arguments = argv;
1001 	*argument_count = argc;
1002 
1003 	if (Quote) {
1004 		return NULL;
1005 	}
1006 
1007 	/* Return new scanning cursor:
1008 	   pointer to char after closing bracket */
1009 	return getP;
1010 }
1011 
1012 
1013 /*
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
1017  * in the buffer.
1018  */
1019 int
parse(char ** buf,long i,char * tag,char * (* func)(long,const char **))1020 parse(
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' */
1025 	)
1026 {
1027 	/* the name of the vairable ... */
1028 	char *val;
1029 	long valln;
1030 	char **args;
1031 	char *end;
1032 	long end_offset;
1033 	int  argc;
1034 	size_t taglen = strlen(tag);
1035 
1036 	/* Current position in buffer should start with 'tag' */
1037 	if (strncmp((*buf)+i, tag, taglen) != 0) {
1038 		return 0;
1039 	}
1040 	/* .. and match exactly (a whitespace following 'tag') */
1041 	if (! isspace(*((*buf) + i + taglen)) ) {
1042 		return 0;
1043 	}
1044 
1045 #ifdef DEBUG_PARSER
1046 	printf("parse(): handling tag '%s'\n", tag);
1047 #endif
1048 
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);
1053 	if (end)
1054 	{
1055 		/* got arguments, call function for 'tag' with arguments */
1056 		val = func(argc, (const char **) args);
1057 		free(args);
1058 	}
1059 	else
1060 	{
1061 		/* unable to parse arguments, undo 0-termination by scanargs */
1062 		for (; argc > 0; argc--) {
1063 			*((args[argc-1])-1) = ' ';
1064 		}
1065 
1066 		/* next call, try parsing at current offset +1 */
1067 		end = (*buf) + i + 1;
1068 
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");
1072 	}
1073 
1074 	/* remember offset where we have to continue parsing */
1075 	end_offset = end - (*buf);
1076 
1077 	valln = 0;
1078 	if (val) {
1079 		valln = strlen(val);
1080 	}
1081 
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);
1090 
1091 		if (*buf == NULL) {
1092 			perror("Realoc buf:");
1093 			exit(1);
1094 		};
1095 	}
1096 
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;
1101 
1102 	/* splice the variable:
1103 	   step 1. Shift pending data to make room for 'val' */
1104 	memmove((*buf) + i + valln, end, strlen(end) + 1);
1105 
1106 	/* step 2. Insert val */
1107 	if (val) {
1108 		memmove((*buf)+i, val, valln);
1109 		free(val);
1110 	}
1111 	return (valln > 0 ? valln-1: valln);
1112 }
1113 
1114 char *
http_time(time_t * now)1115 http_time(time_t *now) {
1116         struct tm *tmptime;
1117         static char buf[60];
1118 
1119         tmptime=gmtime(now);
1120         strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
1121         return(buf);
1122 }
1123 
rrdcgiHeader(void)1124 void rrdcgiHeader(void)
1125 {
1126     if (rrdcgiType)
1127 	printf ("Content-type: %s\n", rrdcgiType);
1128     else
1129 	printf ("Content-type: text/html\n");
1130     if (rrdcgiHeaderString)
1131 	printf ("%s", rrdcgiHeaderString);
1132     printf ("\n");
1133 }
1134 
rrdcgiDebug(int level,int where)1135 void rrdcgiDebug(int level, int where)
1136 {
1137     if (level > 0)
1138 	rrdcgiDebugLevel = level;
1139     else
1140 	rrdcgiDebugLevel = 0;
1141     if (where)
1142 	rrdcgiDebugStderr = 0;
1143     else
1144 	rrdcgiDebugStderr = 1;
1145 }
1146 
rrdcgiDecodeString(char * text)1147 char *rrdcgiDecodeString(char *text)
1148 {
1149     char *cp, *xp;
1150 
1151     for (cp=text,xp=text; *cp; cp++) {
1152 	if (*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');
1161 		xp++;cp+=2;
1162 	    }
1163 	} else {
1164 	    *(xp++) = *cp;
1165 	}
1166     }
1167     memset(xp, 0, cp-xp);
1168     return text;
1169 }
1170 
1171 /*  rrdcgiReadVariables()
1172  *
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.
1175  */
rrdcgiReadVariables(void)1176 s_var **rrdcgiReadVariables(void)
1177 {
1178     int length;
1179     char *line = NULL;
1180     int numargs;
1181     char *cp, *ip, *esp, *sptr;
1182     s_var **result;
1183     int i, k, len;
1184     char tmp[101];
1185 
1186     cp = getenv("REQUEST_METHOD");
1187     ip = getenv("CONTENT_LENGTH");
1188 
1189     if (cp && !strcmp(cp, "POST")) {
1190 	if (ip) {
1191 	    length = atoi(ip);
1192 	    if ((line = (char *)malloc (length+2)) == NULL)
1193 		return NULL;
1194 	    fgets(line, length+1, stdin);
1195 	} else
1196 	    return NULL;
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)
1201 		return NULL;
1202 	    sprintf (line, "%s", esp);
1203 	} else
1204 	    return NULL;
1205     } else {
1206         length = 0;
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) {
1210 	    if (strlen(tmp)) {
1211 		if (tmp[strlen(tmp)-1] == '\n')
1212 		    tmp[strlen(tmp)-1] = '&';
1213 		if (length) {
1214 		    length += strlen(tmp);
1215 		    len = (length+1) * sizeof(char);
1216 		    if ((line = (char *)realloc (line, len)) == NULL)
1217 		        return NULL;
1218 		    strcat (line, tmp);
1219 		} else {
1220 		    length = strlen(tmp);
1221 		    len = (length+1) * sizeof(char);
1222 		    if ((line = (char *)malloc (len)) == NULL)
1223 		        return NULL;
1224 		    memset (line, 0, len);
1225 		    strcpy (line, tmp);
1226 		}
1227 	    }
1228 	    memset (tmp, 0, sizeof(tmp));
1229 	}
1230 	if (!line)
1231 	    return NULL;
1232 	if (line[strlen(line)-1] == '&')
1233 	    line[strlen(line)-1] = '\0';
1234     }
1235 
1236     /*
1237      *  From now on all cgi variables are stored in the variable line
1238      *  and look like  foo=bar&foobar=barfoo&foofoo=
1239      */
1240 
1241     if (rrdcgiDebugLevel > 0) {
1242 	if (rrdcgiDebugStderr)
1243 	    fprintf (stderr, "Received cgi input: %s\n", line);
1244 	else
1245 	    printf ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n", line);
1246     }
1247 
1248     for (cp=line; *cp; cp++)
1249 	if (*cp == '+')
1250 	    *cp = ' ';
1251 
1252     if (strlen(line)) {
1253 	for (numargs=1,cp=line; *cp; cp++)
1254 	    if (*cp == '&') numargs++;
1255     } else
1256 	numargs = 0;
1257     if (rrdcgiDebugLevel > 0) {
1258 	if (rrdcgiDebugStderr)
1259 	    fprintf (stderr, "%d cgi variables found.\n", numargs);
1260 	else
1261 	    printf ("%d cgi variables found.<br>\n", numargs);
1262     }
1263 
1264     len = (numargs+1) * sizeof(s_var *);
1265     if ((result = (s_var **)malloc (len)) == NULL)
1266 	return NULL;
1267     memset (result, 0, len);
1268 
1269     cp = line;
1270     i=0;
1271     while (*cp) {
1272 	if ((ip = (char *)strchr(cp, '&')) != NULL) {
1273 	    *ip = '\0';
1274 	}else
1275 	    ip = cp + strlen(cp);
1276 
1277 	if ((esp=(char *)strchr(cp, '=')) == NULL) {
1278 	    cp = ++ip;
1279 	    continue;
1280 	}
1281 
1282 	if (!strlen(esp)) {
1283 	    cp = ++ip;
1284 	    continue;
1285 	}
1286 
1287 	if (i<numargs) {
1288 
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) == (size_t)(esp-cp))); k++);
1291 
1292 	    if (k == i) {	/* No such variable yet */
1293 		if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
1294 		    return NULL;
1295 		if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
1296 		    return NULL;
1297 		memset (result[i]->name, 0, esp-cp+1);
1298 		strncpy(result[i]->name, cp, esp-cp);
1299 		cp = ++esp;
1300 		if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
1301 		    return 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);
1308 		    else
1309 			printf ("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n", result[i]->name, result[i]->value);
1310 		}
1311 		i++;
1312 	    } else {	/* There is already such a name, suppose a mutiple field */
1313 		cp = ++esp;
1314 		len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
1315 		if ((sptr = (char *)malloc(len)) == NULL)
1316 		    return 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);
1322 	    }
1323 	}
1324 	cp = ++ip;
1325     }
1326     return result;
1327 }
1328 
1329 /*  rrdcgiInit()
1330  *
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.
1333  */
rrdcgiInit(void)1334 s_cgi *rrdcgiInit(void)
1335 {
1336     s_cgi *res;
1337     s_var **vars;
1338 
1339     vars = rrdcgiReadVariables();
1340 
1341     if (!vars)
1342 	return NULL;
1343 
1344     if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
1345 	return NULL;
1346     res->vars = vars;
1347 
1348     return res;
1349 }
1350 
rrdcgiGetValue(s_cgi * parms,const char * name)1351 char *rrdcgiGetValue(s_cgi *parms, const char *name)
1352 {
1353     int i;
1354 
1355     if (!parms || !parms->vars)
1356 	return NULL;
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);
1362 		else
1363 		    printf ("%s found as %s<br>\n", name, parms->vars[i]->value);
1364 	    }
1365 	    return parms->vars[i]->value;
1366 	}
1367     if (rrdcgiDebugLevel) {
1368 	if (rrdcgiDebugStderr)
1369 	    fprintf (stderr, "%s not found\n", name);
1370 	else
1371 	    printf ("%s not found<br>\n", name);
1372     }
1373     return NULL;
1374 }
1375 
rrdcgiFreeList(char ** list)1376 void rrdcgiFreeList (char **list)
1377 {
1378     int i;
1379 
1380     for (i=0; list[i] != NULL; i++)
1381 	free (list[i]);
1382 	free (list);
1383 }
1384 
rrdcgiFree(s_cgi * parms)1385 void rrdcgiFree (s_cgi *parms)
1386 {
1387     int i;
1388 
1389     if (!parms)
1390 	return;
1391     if (parms->vars) {
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]);
1398 		}
1399 		free (parms->vars);
1400     }
1401     free (parms);
1402 
1403     if (rrdcgiHeaderString) {
1404 	free (rrdcgiHeaderString);
1405 	rrdcgiHeaderString = NULL;
1406     }
1407     if (rrdcgiType) {
1408 	free (rrdcgiType);
1409 	rrdcgiType = NULL;
1410     }
1411 }
1412 
1413