Code

9f0a0bd879afe822e28e9ec759a782955a597bda
[rrdtool-all.git] / program / src / rrd_cgi.c
1 /*****************************************************************************
2  * RRDtool 1.2.29  Copyright by Tobi Oetiker, 1997-2008
3  *****************************************************************************
4  * rrd_cgi.c  RRD Web Page Generator
5  *****************************************************************************/
7 #include "rrd_tool.h"
10 #define MEMBLK 1024
11 /*#define DEBUG_PARSER
12 #define DEBUG_VARS*/
14 typedef struct var_s {
15         char    *name, *value;
16 } s_var;
18 typedef struct cgi_s {
19         s_var **vars;
20 } s_cgi;
22 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
23    and replace by result of arg[2] call */
24 int parse(char **, long, char *, char *(*)(long , const char **));
26 /**************************************************/
27 /* tag replacers ... they are called from parse   */
28 /* through function pointers                      */
29 /**************************************************/
31 /* return cgi var named arg[0] */ 
32 char* cgiget(long , const char **);
34 /* return a quoted cgi var named arg[0] */ 
35 char* cgigetq(long , const char **);
37 /* return a quoted and sanitized cgi variable */
38 char* cgigetqp(long , const char **);
40 /* call rrd_graph and insert appropriate image tag */
41 char* drawgraph(long, const char **);
43 /* return PRINT functions from last rrd_graph call */
44 char* drawprint(long, const char **);
46 /* pretty-print the <last></last> value for some.rrd via strftime() */
47 char* printtimelast(long, const char **);
49 /* pretty-print current time */
50 char* printtimenow(long, const char **);
52 /* set an environment variable */
53 char* rrdsetenv(long, const char **);
55 /* get an environment variable */
56 char* rrdgetenv(long, const char **);
58 /* include the named file at this point */
59 char* includefile(long, const char **);
61 /* for how long is the output of the cgi valid ? */
62 char* rrdgoodfor(long, const char **);
64 /* return rrdcgi version string */ 
65 char* rrdgetinternal(long, const char **);
67 char* rrdstrip(char *buf);
68 char* scanargs(char *line, int *argc, char ***args);
70 /* format at-time specified times using strftime */
71 char* printstrftime(long, const char**);
73 /** HTTP protocol needs special format, and GMT time **/
74 char *http_time(time_t *);
76 /* return a pointer to newly allocated copy of this string */
77 char *stralloc(const char *);
79 /* global variable for rrdcgi */
80 s_cgi *rrdcgiArg;
82 /* rrdcgiHeader
83  * 
84  *  Prints a valid CGI Header (Content-type...) etc.
85  */
86 void rrdcgiHeader(void);
88 /* rrdcgiDecodeString
89  * decode html escapes
90  */
91  
92 char *rrdcgiDecodeString(char *text);
94 /* rrdcgiDebug
95  * 
96  *  Set/unsets debugging
97  */
98 void rrdcgiDebug(int level, int where);
100 /* rrdcgiInit
101  *
102  *  Reads in variables set via POST or stdin.
103  */
104 s_cgi *rrdcgiInit (void);
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);
113 /* rrdcgiFreeList
114  *
115  * Frees a list as returned by rrdcgiGetVariables()
116  */
117 void rrdcgiFreeList (char **list);
119 /* rrdcgiFree
120  *
121  * Frees the internal data structures
122  */
123 void rrdcgiFree (s_cgi *parms);
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);
133 int rrdcgiDebugLevel = 0;
134 int rrdcgiDebugStderr = 1;
135 char *rrdcgiHeaderString = NULL;
136 char *rrdcgiType = NULL;
138 /* rrd interface to the variable functions {put,get}var() */
139 char* rrdgetvar(long argc, const char **args);
140 char* rrdsetvar(long argc, const char **args);
141 char* rrdsetvarconst(long argc, const char **args);
144 /* variable store: put/get key-value pairs */
145 static int   initvar();
146 static void  donevar();
147 static const char* getvar(const char* varname);
148 static const char* putvar(const char* name, const char* value, int is_const);
150 /* key value pair that makes up an entry in the variable store */
151 typedef struct
153         int is_const;           /* const variable or not */
154         const char* name;       /* variable name */
155         const char* value;      /* variable value */
156 } vardata;
158 /* the variable heap: 
159    start with a heapsize of 10 variables */
160 #define INIT_VARSTORE_SIZE      10
161 static vardata* varheap    = NULL;
162 static size_t varheap_size = 0;
164 /* allocate and initialize variable heap */
165 static int
166 initvar()
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;
178 /* cleanup: free allocated memory */
179 static void
180 donevar()
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         }
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)
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;
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)
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         }
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);
263 #ifdef          DEBUG_VARS
264         printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
265 #endif
266         return varheap[i].value;
269 /* expand those RRD:* directives that can be used recursivly */
270 static char*
271 rrd_expand_vars(char* buffer)
273         int i;
275 #ifdef DEBUG_PARSER
276         printf("expanding variables in '%s'\n", buffer);
277 #endif
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;
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     calcpr=NULL;
309   }
312 /* create freeable version of the string */
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);
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 */
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                 }
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         }
362         if (!filter) {
363                 rrdcgiDebug(0,0);
364                 rrdcgiArg = rrdcgiInit();
365                 server_url = getenv("SERVER_URL");
366         }
368         /* make sure we have one extra argument, 
369            if there are others, we do not care Apache gives several */
371         /* if ( (optind != argc-2 
372            && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL) 
373            && optind != argc-1) { */
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         }
382         if(rrd_test_error()) {
383                 fprintf(stderr, "ERROR: %s\n",rrd_get_error());
384                 exit(1);
385         }
387         /* initialize variable heap */
388         initvar();
390 #ifdef DEBUG_PARSER
391        /* some fake header for testing */
392        printf ("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
393 #endif
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         }
420         if (!filter) {
421                 printf ("Content-Type: text/html\n" 
422                                 "Content-Length: %d\n", 
423                                 strlen(buffer));
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         }
438         /* output result */
439         printf("%s", buffer);
441         /* cleanup */
442         calfree();
443         if (buffer){
444                 free(buffer);
445         }
446         donevar();
447         exit(0);
450 /* remove occurrences of .. this is a general measure to make
451    paths which came in via cgi do not go UP ... */
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]");
470 /* rrd interface to the variable function putvar() */
471 char*
472 rrdsetvar(long argc, const char **args)
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]");
487 /* rrd interface to the variable function putvar() */
488 char*
489 rrdsetvarconst(long argc, const char **args)
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]");
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         }
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         }
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("");
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   }
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
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;
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         }
581         /* Init start and end time */
582         parsetime("end-24h", &start_tv);
583         parsetime("now", &end_tv);
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         }
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         }
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         }
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   }
640 /* make a copy of buf and replace open/close brackets with '_' */
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   }
652   p = buf;
653   while (*p) {
654           if (*p == '<' || *p == '>') {
655                   *p = '_';
656           }
657           p++;
658   }
659   return buf;
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;
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   }
694   return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
697 /* remove occurrences of .. this is a general measure to make
698    paths which came in via cgi do not go UP ... */
700 char* cgigetqp(long argc, const char **args){
701        char* buf;
702     char* buf2;
703     char* p;
704         char* d;
706         if (argc < 1)
707         {
708                 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
709         }
711         buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
712     if (!buf)
713         {
714                 return NULL;
715         }
717         buf2 = malloc(strlen(buf)+1);
718     if (!buf2)
719         {
720                 perror("cgigetqp(): Malloc Path Buffer");
721                 exit(1);
722     };
724     p = buf;
725     d = buf2;
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     }
742     *d = 0;
743     free(buf);
745     /* Make sure the path is relative, e.g. does not start with '/' */
746     p = buf2;
747     while ('/' == *p)
748         {
749             *p++ = '_';
750     }
752     return buf2;
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]");
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;
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]");
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]");
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]");
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)
855         char    *getP;          /* read cursor */
856         char    *putP;          /* write cursor */
857         char    Quote;          /* type of quote if in quoted string, 0 otherwise */
858         int     tagcount;       /* open tag count */
859         int     in_arg;         /* if we currently are parsing an argument or not */
860         int     argsz;          /* argument array size */
861         int             curarg_contains_rrd_directives;
863         /* local array of arguments while parsing */
864         int argc = 0;
865         char** argv;
867 #ifdef DEBUG_PARSER
868         printf("<-- scanargs(%s) -->\n", line);
869 #endif
871         *arguments = NULL;
872         *argument_count = 0;
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         }
881         /* skip leading blanks */
882         while (isspace((int)*line)) {
883                 line++;
884         }
886         getP = line;
887         putP = line;
889         Quote    = 0;
890         in_arg   = 0;
891         tagcount = 0;
893         curarg_contains_rrd_directives = 0;
895         /* start parsing 'line' for arguments */
896         while (*getP)
897         {
898                 unsigned char c = *getP++;
900                 if (c == '>' && !Quote && !tagcount) {
901                         /* this is our closing tag, quit scanning */
902                         break;
903                 }
905                 /* remove all special chars */
906                 if (c < ' ') {
907                         c = ' ';
908                 }
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;
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;
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                 }
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         }
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         }
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
999         /* update caller's notion of the argument array and it's size */
1000         *arguments = argv;
1001         *argument_count = argc;
1003         if (Quote) {
1004                 return NULL;
1005         }
1007         /* Return new scanning cursor:
1008            pointer to char after closing bracket */
1009         return getP;
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         )
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);
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         }
1045 #ifdef DEBUG_PARSER
1046         printf("parse(): handling tag '%s'\n", tag);
1047 #endif
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                 }
1066                 /* next call, try parsing at current offset +1 */
1067                 end = (*buf) + i + 1;
1069                 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1070                                                 " Check original file. This may have been altered "
1071                                                 "by parsing.]\n\n");
1072         }
1074         /* remember offset where we have to continue parsing */
1075         end_offset = end - (*buf);
1077         valln = 0;
1078         if (val) {
1079                 valln = strlen(val);
1080         }
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);
1091                 if (*buf == NULL) {
1092                         perror("Realoc buf:");
1093                         exit(1);
1094                 };
1095         }
1097         /* Update new end pointer:
1098            make sure the 'end' pointer gets moved along with the 
1099            buf pointer when realloc moves memory ... */
1100         end = (*buf) + end_offset; 
1102         /* splice the variable:
1103            step 1. Shift pending data to make room for 'val' */
1104         memmove((*buf) + i + valln, end, strlen(end) + 1);
1106         /* step 2. Insert val */
1107         if (val) {
1108                 memmove((*buf)+i, val, valln);
1109                 free(val);
1110         }
1111         return (valln > 0 ? valln-1: valln);
1114 char *
1115 http_time(time_t *now) {
1116         struct tm *tmptime;
1117         static char buf[60];
1119         tmptime=gmtime(now);
1120         strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
1121         return(buf);
1124 void rrdcgiHeader(void)
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");
1135 void rrdcgiDebug(int level, int where)
1137     if (level > 0)
1138         rrdcgiDebugLevel = level;
1139     else
1140         rrdcgiDebugLevel = 0;
1141     if (where)
1142         rrdcgiDebugStderr = 0;
1143     else
1144         rrdcgiDebugStderr = 1;
1147 char *rrdcgiDecodeString(char *text)
1149     char *cp, *xp;
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;
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)
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];
1186     cp = getenv("REQUEST_METHOD");
1187     ip = getenv("CONTENT_LENGTH");
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     }
1236     /*
1237      *  From now on all cgi variables are stored in the variable line
1238      *  and look like  foo=bar&foobar=barfoo&foofoo=
1239      */
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     }
1248     for (cp=line; *cp; cp++)
1249         if (*cp == '+')
1250             *cp = ' ';
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     }
1264     len = (numargs+1) * sizeof(s_var *);
1265     if ((result = (s_var **)malloc (len)) == NULL)
1266         return NULL;
1267     memset (result, 0, len);
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);
1277         if ((esp=(char *)strchr(cp, '=')) == NULL) {
1278             cp = ++ip;
1279             continue;
1280         }
1282         if (!strlen(esp)) {
1283             cp = ++ip;
1284             continue;
1285         }
1287         if (i<numargs) {
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++);
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;
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)
1336     s_cgi *res;
1337     s_var **vars;
1339     vars = rrdcgiReadVariables();
1341     if (!vars)
1342         return NULL;
1344     if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
1345         return NULL;
1346     res->vars = vars;
1348     return res;
1351 char *rrdcgiGetValue(s_cgi *parms, const char *name)
1353     int i;
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;
1376 void rrdcgiFreeList (char **list)
1378     int i;
1380     for (i=0; list[i] != NULL; i++)
1381         free (list[i]);
1382         free (list);
1385 void rrdcgiFree (s_cgi *parms)
1387     int i;
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);
1403     if (rrdcgiHeaderString) {
1404         free (rrdcgiHeaderString);
1405         rrdcgiHeaderString = NULL;
1406     }
1407     if (rrdcgiType) {
1408         free (rrdcgiType);
1409         rrdcgiType = NULL;
1410     }