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