prepare for the release of rrdtool-1.2.20
[rrdtool.git] / src / rrd_cgi.c
index 9e6aaa2..c1869e0 100644 (file)
@@ -1,20 +1,23 @@
 /*****************************************************************************
- * RRDtool 1.2rc8  Copyright by Tobi Oetiker, 1997-2005
+ * RRDtool 1.2.20  Copyright by Tobi Oetiker, 1997-2007
  *****************************************************************************
  * rrd_cgi.c  RRD Web Page Generator
  *****************************************************************************/
 
 #include "rrd_tool.h"
-#include <cgi.h>
-#include <time.h>
 
 
 #define MEMBLK 1024
 /*#define DEBUG_PARSER
 #define DEBUG_VARS*/
 
-/* global variable for libcgi */
-s_cgi *cgiArg;
+typedef struct var_s {
+       char    *name, *value;
+} s_var;
+
+typedef struct cgi_s {
+       s_var **vars;
+} s_cgi;
 
 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
    and replace by result of arg[2] call */
@@ -35,7 +38,7 @@ char* cgigetq(long , const char **);
 char* cgigetqp(long , const char **);
 
 /* call rrd_graph and insert appropriate image tag */
-char* drawgraph(long, char **);
+char* drawgraph(long, const char **);
 
 /* return PRINT functions from last rrd_graph call */
 char* drawprint(long, const char **);
@@ -58,6 +61,9 @@ char* includefile(long, const char **);
 /* for how long is the output of the cgi valid ? */
 char* rrdgoodfor(long, const char **);
 
+/* return rrdcgi version string */ 
+char* rrdgetinternal(long, const char **);
+
 char* rrdstrip(char *buf);
 char* scanargs(char *line, int *argc, char ***args);
 
@@ -70,6 +76,64 @@ char *http_time(time_t *);
 /* return a pointer to newly allocated copy of this string */
 char *stralloc(const char *);
 
+/* global variable for rrdcgi */
+s_cgi *rrdcgiArg;
+
+/* rrdcgiHeader
+ * 
+ *  Prints a valid CGI Header (Content-type...) etc.
+ */
+void rrdcgiHeader(void);
+
+/* rrdcgiDecodeString
+ * decode html escapes
+ */
+char *rrdcgiDecodeString(char *text);
+
+/* rrdcgiDebug
+ * 
+ *  Set/unsets debugging
+ */
+void rrdcgiDebug(int level, int where);
+
+/* rrdcgiInit
+ *
+ *  Reads in variables set via POST or stdin.
+ */
+s_cgi *rrdcgiInit (void);
+
+/* rrdcgiGetValue
+ *
+ *  Returns the value of the specified variable or NULL if it's empty
+ *  or doesn't exist.
+ */
+char *rrdcgiGetValue (s_cgi *parms, const char *name);
+
+/* rrdcgiFreeList
+ *
+ * Frees a list as returned by rrdcgiGetVariables()
+ */
+void rrdcgiFreeList (char **list);
+
+/* rrdcgiFree
+ *
+ * Frees the internal data structures
+ */
+void rrdcgiFree (s_cgi *parms);
+
+/*  rrdcgiReadVariables()
+ *
+ *  Read from stdin if no string is provided via CGI.  Variables that
+ *  doesn't have a value associated with it doesn't get stored.
+ */
+s_var **rrdcgiReadVariables(void);
+
+
+int rrdcgiDebugLevel = 0;
+int rrdcgiDebugStderr = 1;
+char *rrdcgiHeaderString = NULL;
+char *rrdcgiType = NULL;
 
 /* rrd interface to the variable functions {put,get}var() */
 char* rrdgetvar(long argc, const char **args);
@@ -223,6 +287,7 @@ rrd_expand_vars(char* buffer)
                 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
                 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
                 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
+               parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
        }
        return buffer;
 }
@@ -266,6 +331,8 @@ int main(int argc, char *argv[]) {
 #ifdef MUST_DISABLE_FPMASK
        fpsetmask(0);
 #endif
+        optind = 0; opterr = 0;  /* initialize getopt */
+
        /* what do we get for cmdline arguments?
        for (i=0;i<argc;i++)
        printf("%d-'%s'\n",i,argv[i]); */
@@ -292,8 +359,8 @@ int main(int argc, char *argv[]) {
        }
 
        if (!filter) {
-               cgiDebug(0,0);
-               cgiArg = cgiInit();
+               rrdcgiDebug(0,0);
+               rrdcgiArg = rrdcgiInit();
                server_url = getenv("SERVER_URL");
        }
 
@@ -346,6 +413,7 @@ int main(int argc, char *argv[]) {
                parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
                parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
                parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
+               parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
        }
 
        if (!filter) {
@@ -443,11 +511,7 @@ char* rrdgetenv(long argc, const char **args) {
        if (envvar) {
                return stralloc(envvar);
        } else {
-#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
-               _snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
-#else
                 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
-#endif         
                 return stralloc(buf);
        }
 }
@@ -463,11 +527,7 @@ char* rrdgetvar(long argc, const char **args) {
        if (value) {
                return stralloc(value);
        } else {
-#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
-               _snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
-#else
                 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
-#endif
                return stralloc(buf);
        }
 }
@@ -486,6 +546,20 @@ char* rrdgoodfor(long argc, const char **args){
   return stralloc("");
 }
 
+char* rrdgetinternal(long argc, const char **args){
+  if (argc == 1) {
+    if( strcasecmp( args[0], "VERSION") == 0) {
+      return stralloc(PACKAGE_VERSION);
+    } else if( strcasecmp( args[0], "COMPILETIME") == 0) {
+      return stralloc(__DATE__ " " __TIME__);
+    } else {
+      return stralloc("[ERROR: internal unknown argument]");
+    }
+  } else {
+    return stralloc("[ERROR: internal expected 1 argument]");
+  }
+}
+
 /* Format start or end times using strftime.  We always need both the
  * start and end times, because, either might be relative to the other.
  * */
@@ -500,7 +574,7 @@ char* printstrftime(long argc, const char **args){
        /* Make sure that we were given the right number of args */
        if( argc != 4) {
                rrd_set_error( "wrong number of args %d", argc);
-               return (char *) -1;
+               return stralloc("");
        }
 
        /* Init start and end time */
@@ -510,14 +584,14 @@ char* printstrftime(long argc, const char **args){
        /* Parse the start and end times we were given */
        if( (parsetime_error = parsetime( args[1], &start_tv))) {
                rrd_set_error( "start time: %s", parsetime_error);
-               return (char *) -1;
+               return stralloc("");
        }
        if( (parsetime_error = parsetime( args[2], &end_tv))) {
                rrd_set_error( "end time: %s", parsetime_error);
-               return (char *) -1;
+               return stralloc("");
        }
        if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
-               return (char *) -1;
+               return stralloc("");
        }
 
        /* Do we do the start or end */
@@ -529,7 +603,7 @@ char* printstrftime(long argc, const char **args){
        }
        else {
                rrd_set_error( "start/end not found in '%s'", args[0]);
-               return (char *) -1;
+               return stralloc("");
        }
 
        /* now format it */
@@ -538,14 +612,14 @@ char* printstrftime(long argc, const char **args){
        }
        else {
                rrd_set_error( "strftime failed");
-               return (char *) -1;
+               return stralloc("");
        }
 }
 
 char* includefile(long argc, const char **args){
   char *buffer;
   if (argc >= 1) {
-      char* filename = args[0];
+      const char* filename = args[0];
       readfile(filename, &buffer, 0);
       if (rrd_test_error()) {
                char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE));
@@ -586,7 +660,7 @@ char* rrdstrip(char *buf) {
 
 char* cgigetq(long argc, const char **args){
   if (argc>= 1){
-    char *buf = rrdstrip(cgiGetValue(cgiArg,args[0]));
+    char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
     char *buf2;
     char *c,*d;
     int  qc=0;
@@ -633,7 +707,7 @@ char* cgigetqp(long argc, const char **args){
                 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
         }
 
-        buf = rrdstrip(cgiGetValue(cgiArg, args[0]));
+        buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
     if (!buf)
         {
                 return NULL;
@@ -680,14 +754,14 @@ char* cgigetqp(long argc, const char **args){
 
 char* cgiget(long argc, const char **args){
   if (argc>= 1)
-    return rrdstrip(cgiGetValue(cgiArg,args[0]));
+    return rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
   else
     return stralloc("[ERROR: not enough arguments for RRD::CV]");
 }
 
 
 
-char* drawgraph(long argc, char **args){
+char* drawgraph(long argc, const char **args){
   int i,xsize, ysize;
   double ymin,ymax;
   for(i=0;i<argc;i++)
@@ -696,10 +770,8 @@ char* drawgraph(long argc, char **args){
     args[argc++] = "--imginfo";
     args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
   }
-  optind=0; /* reset gnu getopt */
-  opterr=0; /* reset gnu getopt */
   calfree();
-  if( rrd_graph(argc+1, args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
+  if( rrd_graph(argc+1, (char **) args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
     return stralloc(calcpr[0]);
   } else {
     if (rrd_test_error()) {
@@ -732,7 +804,7 @@ char* printtimelast(long argc, const char **args) {
     if (buf == NULL){  
        return stralloc("[ERROR: allocating strftime buffer]");
     };
-    last = rrd_last(argc+1, args-1); 
+    last = rrd_last(argc+1, (char **) args-1); 
     if (rrd_test_error()) {
       char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
       sprintf(err, "[ERROR: %s]",rrd_get_error());
@@ -981,7 +1053,7 @@ parse(
        if (end)
        {
                /* got arguments, call function for 'tag' with arguments */
-               val = func(argc, args);
+               val = func(argc, (const char **) args);
                free(args);
        }
        else
@@ -1048,3 +1120,293 @@ http_time(time_t *now) {
         strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
         return(buf);
 }
+
+void rrdcgiHeader(void)
+{
+    if (rrdcgiType)
+       printf ("Content-type: %s\n", rrdcgiType);
+    else
+       printf ("Content-type: text/html\n");
+    if (rrdcgiHeaderString)
+       printf ("%s", rrdcgiHeaderString);
+    printf ("\n");
+}
+
+void rrdcgiDebug(int level, int where)
+{
+    if (level > 0)
+       rrdcgiDebugLevel = level;
+    else
+       rrdcgiDebugLevel = 0;
+    if (where)
+       rrdcgiDebugStderr = 0;
+    else
+       rrdcgiDebugStderr = 1;
+}
+
+char *rrdcgiDecodeString(char *text)
+{
+    char *cp, *xp;
+
+    for (cp=text,xp=text; *cp; cp++) {
+       if (*cp == '%') {
+           if (strchr("0123456789ABCDEFabcdef", *(cp+1))
+               && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
+               if (islower(*(cp+1)))
+                   *(cp+1) = toupper(*(cp+1));
+               if (islower(*(cp+2)))
+                   *(cp+2) = toupper(*(cp+2));
+               *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
+                   + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
+               xp++;cp+=2;
+           }
+       } else {
+           *(xp++) = *cp;
+       }
+    }
+    memset(xp, 0, cp-xp);
+    return text;
+}
+
+/*  rrdcgiReadVariables()
+ *
+ *  Read from stdin if no string is provided via CGI.  Variables that
+ *  doesn't have a value associated with it doesn't get stored.
+ */
+s_var **rrdcgiReadVariables(void)
+{
+    int length;
+    char *line = NULL;
+    int numargs;
+    char *cp, *ip, *esp, *sptr;
+    s_var **result;
+    int i, k, len;
+    char tmp[101];
+
+    cp = getenv("REQUEST_METHOD");
+    ip = getenv("CONTENT_LENGTH");
+
+    if (cp && !strcmp(cp, "POST")) {
+       if (ip) {
+           length = atoi(ip);
+           if ((line = (char *)malloc (length+2)) == NULL)
+               return NULL;
+           fgets(line, length+1, stdin);
+       } else
+           return NULL;
+    } else if (cp && !strcmp(cp, "GET")) {
+       esp = getenv("QUERY_STRING");
+       if (esp && strlen(esp)) {
+           if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
+               return NULL;
+           sprintf (line, "%s", esp);
+       } else
+           return NULL;
+    } else {
+        length = 0;
+       printf ("(offline mode: enter name=value pairs on standard input)\n");
+       memset (tmp, 0, sizeof(tmp));
+       while((cp = fgets (tmp, 100, stdin)) != NULL) {
+           if (strlen(tmp)) {
+               if (tmp[strlen(tmp)-1] == '\n')
+                   tmp[strlen(tmp)-1] = '&';
+               if (length) {
+                   length += strlen(tmp);
+                   len = (length+1) * sizeof(char);
+                   if ((line = (char *)realloc (line, len)) == NULL)
+                       return NULL;
+                   strcat (line, tmp);
+               } else {
+                   length = strlen(tmp);
+                   len = (length+1) * sizeof(char);
+                   if ((line = (char *)malloc (len)) == NULL)
+                       return NULL;
+                   memset (line, 0, len);
+                   strcpy (line, tmp);
+               }
+           }
+           memset (tmp, 0, sizeof(tmp));
+       }
+       if (!line)
+           return NULL;
+       if (line[strlen(line)-1] == '&')
+           line[strlen(line)-1] = '\0';
+    }
+
+    /*
+     *  From now on all cgi variables are stored in the variable line
+     *  and look like  foo=bar&foobar=barfoo&foofoo=
+     */
+
+    if (rrdcgiDebugLevel > 0) {
+       if (rrdcgiDebugStderr)
+           fprintf (stderr, "Received cgi input: %s\n", line);
+       else
+           printf ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n", line);
+    }
+
+    for (cp=line; *cp; cp++)
+       if (*cp == '+')
+           *cp = ' ';
+
+    if (strlen(line)) {
+       for (numargs=1,cp=line; *cp; cp++)
+           if (*cp == '&') numargs++;
+    } else
+       numargs = 0;
+    if (rrdcgiDebugLevel > 0) {
+       if (rrdcgiDebugStderr)
+           fprintf (stderr, "%d cgi variables found.\n", numargs);
+       else
+           printf ("%d cgi variables found.<br>\n", numargs);
+    }
+
+    len = (numargs+1) * sizeof(s_var *);
+    if ((result = (s_var **)malloc (len)) == NULL)
+       return NULL;
+    memset (result, 0, len);
+
+    cp = line;
+    i=0;
+    while (*cp) {
+       if ((ip = (char *)strchr(cp, '&')) != NULL) {
+           *ip = '\0';
+       }else
+           ip = cp + strlen(cp);
+
+       if ((esp=(char *)strchr(cp, '=')) == NULL) {
+           cp = ++ip;
+           continue;
+       }
+
+       if (!strlen(esp)) {
+           cp = ++ip;
+           continue;
+       }
+
+       if (i<numargs) {
+
+           /* try to find out if there's already such a variable */
+           for (k=0; k<i && (strncmp (result[k]->name,cp, esp-cp) || !(strlen (result[k]->name) == esp-cp)); k++);
+
+           if (k == i) {       /* No such variable yet */
+               if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
+                   return NULL;
+               if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
+                   return NULL;
+               memset (result[i]->name, 0, esp-cp+1);
+               strncpy(result[i]->name, cp, esp-cp);
+               cp = ++esp;
+               if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
+                   return NULL;
+               memset (result[i]->value, 0, ip-esp+1);
+               strncpy(result[i]->value, cp, ip-esp);
+               result[i]->value = rrdcgiDecodeString(result[i]->value);
+               if (rrdcgiDebugLevel) {
+                   if (rrdcgiDebugStderr)
+                       fprintf (stderr, "%s: %s\n", result[i]->name, result[i]->value);
+                   else
+                       printf ("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n", result[i]->name, result[i]->value);
+               }
+               i++;
+           } else {    /* There is already such a name, suppose a mutiple field */
+               cp = ++esp;
+               len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
+               if ((sptr = (char *)malloc(len)) == NULL)
+                   return NULL;
+               memset (sptr, 0, len);
+               sprintf (sptr, "%s\n", result[k]->value);
+               strncat(sptr, cp, ip-esp);
+               free(result[k]->value);
+               result[k]->value = rrdcgiDecodeString (sptr);
+           }
+       }
+       cp = ++ip;
+    }
+    return result;
+}
+
+/*  rrdcgiInit()
+ *
+ *  Read from stdin if no string is provided via CGI.  Variables that
+ *  doesn't have a value associated with it doesn't get stored.
+ */
+s_cgi *rrdcgiInit(void)
+{
+    s_cgi *res;
+    s_var **vars;
+
+    vars = rrdcgiReadVariables();
+
+    if (!vars)
+       return NULL;
+
+    if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
+       return NULL;
+    res->vars = vars;
+
+    return res;
+}
+
+char *rrdcgiGetValue(s_cgi *parms, const char *name)
+{
+    int i;
+
+    if (!parms || !parms->vars)
+       return NULL;
+    for (i=0;parms->vars[i]; i++)
+       if (!strcmp(name,parms->vars[i]->name)) {
+           if (rrdcgiDebugLevel > 0) {
+               if (rrdcgiDebugStderr)
+                   fprintf (stderr, "%s found as %s\n", name, parms->vars[i]->value);
+               else
+                   printf ("%s found as %s<br>\n", name, parms->vars[i]->value);
+           }
+           return parms->vars[i]->value;
+       }
+    if (rrdcgiDebugLevel) {
+       if (rrdcgiDebugStderr)
+           fprintf (stderr, "%s not found\n", name);
+       else
+           printf ("%s not found<br>\n", name);
+    }
+    return NULL;
+}
+
+void rrdcgiFreeList (char **list)
+{
+    int i;
+
+    for (i=0; list[i] != NULL; i++)
+       free (list[i]);
+       free (list);
+}
+
+void rrdcgiFree (s_cgi *parms)
+{
+    int i;
+
+    if (!parms)
+       return;
+    if (parms->vars) {
+               for (i=0;parms->vars[i]; i++) {
+                       if (parms->vars[i]->name)
+                               free (parms->vars[i]->name);
+                       if (parms->vars[i]->value)
+                               free (parms->vars[i]->value);
+           free (parms->vars[i]);
+               }
+               free (parms->vars);
+    }
+    free (parms);
+
+    if (rrdcgiHeaderString) {
+       free (rrdcgiHeaderString);
+       rrdcgiHeaderString = NULL;
+    }
+    if (rrdcgiType) {
+       free (rrdcgiType);
+       rrdcgiType = NULL;
+    }
+}
+