prepare for the release of rrdtool-1.2.19
[rrdtool.git] / src / rrd_cgi.c
1 /*****************************************************************************
2  * RRDtool 1.2.19  Copyright by Tobi Oetiker, 1997-2007
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
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
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*
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*
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*
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;
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   }
309 }
310
311 /* create freeable version of the string */
312 char * stralloc(const char *str){
313   char* nstr;
314   if (!str) {
315           return NULL;
316   }
317   nstr = malloc((strlen(str)+1));
318   strcpy(nstr,str);
319   return(nstr);
320 }
321
322 int main(int argc, char *argv[]) {
323         long length;
324         char *buffer;
325         char *server_url = NULL;
326         long i;
327         long filter=0;
328 #ifdef MUST_DISABLE_SIGFPE
329         signal(SIGFPE,SIG_IGN);
330 #endif
331 #ifdef MUST_DISABLE_FPMASK
332         fpsetmask(0);
333 #endif
334         optind = 0; opterr = 0;  /* initialize getopt */
335
336         /* what do we get for cmdline arguments?
337         for (i=0;i<argc;i++)
338         printf("%d-'%s'\n",i,argv[i]); */
339         while (1) {
340                 static struct option long_options[] = {
341                         { "filter", no_argument, 0, 'f' },
342                         { 0, 0, 0, 0}
343                 };
344                 int option_index = 0;
345                 int opt;
346                 opt = getopt_long(argc, argv, "f", long_options, &option_index);
347                 if (opt == EOF) {
348                         break;
349                 }
350
351                 switch(opt) {
352                 case 'f':
353                                 filter=1;
354                         break;
355                 case '?':
356                         printf("unknown commandline option '%s'\n",argv[optind-1]);
357                         return -1;
358                 }
359         }
360
361         if (!filter) {
362                 rrdcgiDebug(0,0);
363                 rrdcgiArg = rrdcgiInit();
364                 server_url = getenv("SERVER_URL");
365         }
366
367         /* make sure we have one extra argument, 
368            if there are others, we do not care Apache gives several */
369
370         /* if ( (optind != argc-2 
371            && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL) 
372            && optind != argc-1) { */
373
374         if ( optind >= argc ) { 
375                 fprintf(stderr, "ERROR: expected a filename\n");
376                 exit(1);
377         } else {
378                 length = readfile(argv[optind], &buffer, 1);
379         }
380
381         if(rrd_test_error()) {
382                 fprintf(stderr, "ERROR: %s\n",rrd_get_error());
383                 exit(1);
384         }
385
386         /* initialize variable heap */
387         initvar();
388
389 #ifdef DEBUG_PARSER
390        /* some fake header for testing */
391        printf ("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
392 #endif
393
394
395         /* expand rrd directives in buffer recursivly */
396         for (i=0; buffer[i]; i++) {
397                 if (buffer[i] != '<')
398                         continue;
399                 if (!filter) {
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);
404                 }
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);
417         }
418
419         if (!filter) {
420                 printf ("Content-Type: text/html\n" 
421                                 "Content-Length: %d\n", 
422                                 strlen(buffer));
423
424                 if (labs(goodfor) > 0) {
425                         time_t now;
426                         now = time(NULL);
427                         printf("Last-Modified: %s\n", http_time(&now));
428                         now += labs(goodfor);
429                         printf("Expires: %s\n", http_time(&now));
430                         if (goodfor < 0) {
431                                 printf("Refresh: %ld\n", labs(goodfor));
432                         }
433                 }
434                 printf("\n");
435         }
436
437         /* output result */
438         printf("%s", buffer);
439
440         /* cleanup */
441         calfree();
442         if (buffer){
443                 free(buffer);
444         }
445         donevar();
446         exit(0);
447 }
448
449 /* remove occurrences of .. this is a general measure to make
450    paths which came in via cgi do not go UP ... */
451
452 char* rrdsetenv(long argc, const char **args) {
453         if (argc >= 2) {
454                 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
455                 if (xyz == NULL) {
456                         return stralloc("[ERROR: allocating setenv buffer]");
457                 };
458                 sprintf(xyz, "%s=%s", args[0], args[1]);
459                 if(putenv(xyz) == -1) {
460                         free(xyz);
461                         return stralloc("[ERROR: failed to do putenv]");
462                 };
463                 return stralloc("");
464         }
465         return stralloc("[ERROR: setenv failed because not enough "
466                                         "arguments were defined]");
467 }
468
469 /* rrd interface to the variable function putvar() */
470 char*
471 rrdsetvar(long argc, const char **args)
472 {
473         if (argc >= 2)
474         {
475                 const char* result = putvar(args[0], args[1], 0 /* not const */);
476                 if (result) {
477                         /* setvar does not return the value set */
478                         return stralloc("");
479                 }
480                 return stralloc("[ERROR: putvar failed]");
481         }
482         return stralloc("[ERROR: putvar failed because not enough arguments "
483                                         "were defined]");
484 }
485
486 /* rrd interface to the variable function putvar() */
487 char*
488 rrdsetvarconst(long argc, const char **args)
489 {
490         if (argc >= 2)
491         {
492                 const char* result = putvar(args[0], args[1], 1 /* const */);
493                 if (result) {
494                         /* setvar does not return the value set */
495                         return stralloc("");
496                 }
497                 return stralloc("[ERROR: putvar failed]");
498         }
499         return stralloc("[ERROR: putvar failed because not enough arguments "
500                                         "were defined]");
501 }
502
503 char* rrdgetenv(long argc, const char **args) {
504         char buf[128];
505         const char* envvar;
506         if (argc != 1) {
507                 return stralloc("[ERROR: getenv failed because it did not "
508                                                 "get 1 argument only]");
509         };
510         envvar = getenv(args[0]);
511         if (envvar) {
512                 return stralloc(envvar);
513         } else {
514                 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
515                 return stralloc(buf);
516         }
517 }
518
519 char* rrdgetvar(long argc, const char **args) {
520         char buf[128];
521         const char* value;
522         if (argc != 1) {
523                 return stralloc("[ERROR: getvar failed because it did not "
524                                                 "get 1 argument only]");
525         };
526         value = getvar(args[0]);
527         if (value) {
528                 return stralloc(value);
529         } else {
530                 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
531                 return stralloc(buf);
532         }
533 }
534
535 char* rrdgoodfor(long argc, const char **args){
536   if (argc == 1) {
537       goodfor = atol(args[0]);
538   } else {
539     return stralloc("[ERROR: goodfor expected 1 argument]");
540   }
541    
542   if (goodfor == 0){
543      return stralloc("[ERROR: goodfor value must not be 0]");
544   }
545    
546   return stralloc("");
547 }
548
549 char* rrdgetinternal(long argc, const char **args){
550   if (argc == 1) {
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__);
555     } else {
556       return stralloc("[ERROR: internal unknown argument]");
557     }
558   } else {
559     return stralloc("[ERROR: internal expected 1 argument]");
560   }
561 }
562
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.
565  * */
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];
571         struct tm *the_tm;
572         time_t  start_tmp, end_tmp;
573
574         /* Make sure that we were given the right number of args */
575         if( argc != 4) {
576                 rrd_set_error( "wrong number of args %d", argc);
577                 return (char *) -1;
578         }
579
580         /* Init start and end time */
581         parsetime("end-24h", &start_tv);
582         parsetime("now", &end_tv);
583
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);
587                 return (char *) -1;
588         }
589         if( (parsetime_error = parsetime( args[2], &end_tv))) {
590                 rrd_set_error( "end time: %s", parsetime_error);
591                 return (char *) -1;
592         }
593         if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
594                 return (char *) -1;
595         }
596
597         /* Do we do the start or end */
598         if( strcasecmp( args[0], "START") == 0) {
599                 the_tm = localtime( &start_tmp);
600         }
601         else if( strcasecmp( args[0], "END") == 0) {
602                 the_tm = localtime( &end_tmp);
603         }
604         else {
605                 rrd_set_error( "start/end not found in '%s'", args[0]);
606                 return (char *) -1;
607         }
608
609         /* now format it */
610         if( strftime( formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
611                 return( stralloc( formatted));
612         }
613         else {
614                 rrd_set_error( "strftime failed");
615                 return (char *) -1;
616         }
617 }
618
619 char* includefile(long argc, const char **args){
620   char *buffer;
621   if (argc >= 1) {
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());
627           rrd_clear_error();
628           return err;
629       } else {
630        return buffer;
631       }
632   }
633   else
634   {
635       return stralloc("[ERROR: No Inclue file defined]");
636   }
637 }
638
639 /* make a copy of buf and replace open/close brackets with '_' */
640 char* rrdstrip(char *buf) {
641   char* p;
642   if (buf == NULL) {
643           return NULL;
644   }
645   /* make a copy of the buffer */
646   buf = stralloc(buf);
647   if (buf == NULL) {
648           return NULL;
649   }
650
651   p = buf;
652   while (*p) {
653           if (*p == '<' || *p == '>') {
654                   *p = '_';
655           }
656           p++;
657   }
658   return buf;
659 }
660
661 char* cgigetq(long argc, const char **args){
662   if (argc>= 1){
663     char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
664     char *buf2;
665     char *c,*d;
666     int  qc=0;
667     if (buf==NULL) return NULL;
668
669     for(c=buf;*c != '\0';c++)
670       if (*c == '"') qc++;
671     if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
672         perror("Malloc Buffer");
673         exit(1);
674     };
675     c=buf;
676     d=buf2;
677     *(d++) = '"';
678     while(*c != '\0'){
679         if (*c == '"') {
680             *(d++) = '"';
681             *(d++) = '\'';
682             *(d++) = '"';
683             *(d++) = '\'';
684         } 
685         *(d++) = *(c++);
686     }
687     *(d++) = '"';
688     *(d) = '\0';
689     free(buf);
690     return buf2;
691   }
692
693   return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
694 }
695
696 /* remove occurrences of .. this is a general measure to make
697    paths which came in via cgi do not go UP ... */
698
699 char* cgigetqp(long argc, const char **args){
700        char* buf;
701     char* buf2;
702     char* p;
703         char* d;
704
705         if (argc < 1)
706         {
707                 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
708         }
709
710         buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
711     if (!buf)
712         {
713                 return NULL;
714         }
715
716         buf2 = malloc(strlen(buf)+1);
717     if (!buf2)
718         {
719                 perror("cgigetqp(): Malloc Path Buffer");
720                 exit(1);
721     };
722
723     p = buf;
724     d = buf2;
725
726     while (*p)
727         {
728                 /* prevent mallicious paths from entering the system */
729                 if (p[0] == '.' && p[1] == '.')
730                 {
731                         p += 2;
732                         *d++ = '_';
733                         *d++ = '_';     
734                 }
735                 else
736                 {
737                         *d++ = *p++;
738                 }
739     }
740
741     *d = 0;
742     free(buf);
743
744     /* Make sure the path is relative, e.g. does not start with '/' */
745     p = buf2;
746     while ('/' == *p)
747         {
748             *p++ = '_';
749     }
750
751     return buf2;
752 }
753
754
755 char* cgiget(long argc, const char **args){
756   if (argc>= 1)
757     return rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
758   else
759     return stralloc("[ERROR: not enough arguments for RRD::CV]");
760 }
761
762
763
764 char* drawgraph(long argc, const char **args){
765   int i,xsize, ysize;
766   double ymin,ymax;
767   for(i=0;i<argc;i++)
768     if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
769   if(i==argc) {
770     args[argc++] = "--imginfo";
771     args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
772   }
773   calfree();
774   if( rrd_graph(argc+1, (char **) args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
775     return stralloc(calcpr[0]);
776   } else {
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());
780       rrd_clear_error();
781       calfree();
782       return err;
783     }
784   }
785   return NULL;
786 }
787
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
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
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*
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
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 *
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
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
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
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  */
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) == 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  */
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
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
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
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