use snprintf, strdup, ... where possible to make for safer operation -- Martin Pelikan
[rrdtool.git] / src / rrd_cgi.c
index 800f3a3..a0c9b3b 100644 (file)
@@ -1,5 +1,5 @@
 /*****************************************************************************
- * RRDtool 1.3rc2  Copyright by Tobi Oetiker, 1997-2008
+ * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
  *****************************************************************************
  * rrd_cgi.c  RRD Web Page Generator
  *****************************************************************************/
@@ -9,6 +9,11 @@
 #include <stdlib.h>
 #endif
 
+#ifdef WIN32
+   #define strcasecmp stricmp
+   #define strcasencmp strnicmp
+#endif
+
 #define MEMBLK 1024
 /*#define DEBUG_PARSER
 #define DEBUG_VARS*/
@@ -374,6 +379,7 @@ static void calfree(
         if (calcpr) {
             free(calcpr);
         }
+        calcpr = NULL;
     }
 }
 
@@ -381,14 +387,67 @@ static void calfree(
 char     *stralloc(
     const char *str)
 {
-    char     *nstr;
-
     if (!str) {
         return NULL;
     }
-    nstr = malloc((strlen(str) + 1));
-    strcpy(nstr, str);
-    return (nstr);
+    return strdup(str);
+}
+
+static int readfile(
+    const char *file_name,
+    char **buffer,
+    int skipfirst)
+{
+    long      writecnt = 0, totalcnt = MEMBLK;
+    long      offset = 0;
+    FILE     *input = NULL;
+    char      c;
+
+    if ((strcmp("-", file_name) == 0)) {
+        input = stdin;
+    } else {
+        if ((input = fopen(file_name, "rb")) == NULL) {
+            rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
+            return (-1);
+        }
+    }
+    if (skipfirst) {
+        do {
+            c = getc(input);
+            offset++;
+        } while (c != '\n' && !feof(input));
+    }
+    if (strcmp("-", file_name)) {
+        fseek(input, 0, SEEK_END);
+        /* have extra space for detecting EOF without realloc */
+        totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
+        if (totalcnt < MEMBLK)
+            totalcnt = MEMBLK;  /* sanitize */
+        fseek(input, offset * sizeof(char), SEEK_SET);
+    }
+    if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
+        perror("Allocate Buffer:");
+        exit(1);
+    };
+    do {
+        writecnt +=
+            fread((*buffer) + writecnt, 1,
+                  (totalcnt - writecnt) * sizeof(char), input);
+        if (writecnt >= totalcnt) {
+            totalcnt += MEMBLK;
+            if (((*buffer) =
+                 rrd_realloc((*buffer),
+                             (totalcnt + 4) * sizeof(char))) == NULL) {
+                perror("Realloc Buffer:");
+                exit(1);
+            };
+        }
+    } while (!feof(input));
+    (*buffer)[writecnt] = '\0';
+    if (strcmp("-", file_name) != 0) {
+        fclose(input);
+    };
+    return writecnt;
 }
 
 int main(
@@ -532,12 +591,13 @@ char     *rrdsetenv(
     const char **args)
 {
     if (argc >= 2) {
-        char     *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
+        const size_t len = strlen(args[0]) + strlen(args[1]) + 2;
+        char *xyz = malloc(len);
 
         if (xyz == NULL) {
             return stralloc("[ERROR: allocating setenv buffer]");
         };
-        sprintf(xyz, "%s=%s", args[0], args[1]);
+        snprintf(xyz, len, "%s=%s", args[0], args[1]);
         if (putenv(xyz) == -1) {
             free(xyz);
             return stralloc("[ERROR: failed to do putenv]");
@@ -666,7 +726,7 @@ char     *printstrftime(
     long argc,
     const char **args)
 {
-    struct rrd_time_value start_tv, end_tv;
+    rrd_time_value_t start_tv, end_tv;
     char     *parsetime_error = NULL;
     char      formatted[MAX_STRFTIME_SIZE];
     struct tm *the_tm;
@@ -679,19 +739,19 @@ char     *printstrftime(
     }
 
     /* Init start and end time */
-    parsetime("end-24h", &start_tv);
-    parsetime("now", &end_tv);
+    rrd_parsetime("end-24h", &start_tv);
+    rrd_parsetime("now", &end_tv);
 
     /* Parse the start and end times we were given */
-    if ((parsetime_error = parsetime(args[1], &start_tv))) {
+    if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
         rrd_set_error("start time: %s", parsetime_error);
         return stralloc("");
     }
-    if ((parsetime_error = parsetime(args[2], &end_tv))) {
+    if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
         rrd_set_error("end time: %s", parsetime_error);
         return stralloc("");
     }
-    if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+    if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
         return stralloc("");
     }
 
@@ -725,9 +785,10 @@ char     *includefile(
 
         readfile(filename, &buffer, 0);
         if (rrd_test_error()) {
-            char     *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
+            const size_t len = strlen(rrd_get_error()) + DS_NAM_SIZE;
+            char *err = malloc(len);
 
-            sprintf(err, "[ERROR: %s]", rrd_get_error());
+            snprintf(err, len, "[ERROR: %s]", rrd_get_error());
             rrd_clear_error();
             return err;
         } else {
@@ -891,12 +952,10 @@ char     *drawgraph(
         return stralloc(calcpr[0]);
     } else {
         if (rrd_test_error()) {
-            char     *err =
-                malloc((strlen(rrd_get_error()) +
-                        DS_NAM_SIZE) * sizeof(char));
-            sprintf(err, "[ERROR: %s]", rrd_get_error());
+            const size_t len = strlen(rrd_get_error()) + DS_NAM_SIZE;
+            char *err = malloc(len);
+            snprintf(err, len, "[ERROR: %s]", rrd_get_error());
             rrd_clear_error();
-            calfree();
             return err;
         }
     }
@@ -931,12 +990,14 @@ char     *printtimelast(
         if (buf == NULL) {
             return stralloc("[ERROR: allocating strftime buffer]");
         };
-        last = rrd_last(argc + 1, (char **) args - 1);
+        /* not raising argc in step with args - 1 since the last argument
+           will be used below for strftime  */
+
+        last = rrd_last(argc, (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());
+            const size_t len = strlen(rrd_get_error()) + DS_NAM_SIZE;
+            char *err = malloc(len);
+            snprintf(err, len, "[ERROR: %s]", rrd_get_error());
             rrd_clear_error();
             return err;
         }
@@ -944,10 +1005,7 @@ char     *printtimelast(
         strftime(buf, 254, args[1], &tm_last);
         return buf;
     }
-    if (argc < 2) {
-        return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
-    }
-    return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
+    return stralloc("[ERROR: expected <RRD::TIME::LAST file.rrd strftime-format>]");
 }
 
 char     *printtimenow(
@@ -996,7 +1054,7 @@ char     *scanargs(
     int       curarg_contains_rrd_directives;
 
     /* local array of arguments while parsing */
-    int       argc = 0;
+    int       argc = 1;
     char    **argv;
 
 #ifdef DEBUG_PARSER
@@ -1012,6 +1070,7 @@ char     *scanargs(
     if (!argv) {
         return NULL;
     }
+    argv[0] = "rrdcgi";
 
     /* skip leading blanks */
     while (isspace((int) *line)) {
@@ -1115,7 +1174,7 @@ char     *scanargs(
         argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
     }
 #ifdef DEBUG_PARSER
-    if (argc > 0) {
+    if (argc > 1) {
         int       n;
 
         printf("<-- arguments found [%d]\n", argc);
@@ -1129,8 +1188,17 @@ char     *scanargs(
 #endif
 
     /* update caller's notion of the argument array and it's size */
-    *arguments = argv;
-    *argument_count = argc;
+
+    /* note this is a bit of a hack since the rrd_cgi code used to just put
+       its arguments into a normal array starting at 0 ... since the rrd_*
+       commands expect and argc/argv array we used to just shift everything
+       by -1 ... this in turn exploded when a rrd_* function tried to print
+       argv[0] ... hence we are now doing everything in argv style but hand
+       over seemingly the old array ... but doing argv-1 will actually end
+       up in a 'good' place now. */
+
+    *arguments = argv+1;
+    *argument_count = argc-1;
 
     if (Quote) {
         return NULL;
@@ -1184,13 +1252,8 @@ int parse(
     if (end) {
         /* got arguments, call function for 'tag' with arguments */
         val = func(argc, (const char **) args);
-        free(args);
+        free(args-1);
     } else {
-        /* unable to parse arguments, undo 0-termination by scanargs */
-        for (; argc > 0; argc--) {
-            *((args[argc - 1]) - 1) = ' ';
-        }
-
         /* next call, try parsing at current offset +1 */
         end = (*buf) + i + 1;
 
@@ -1321,6 +1384,7 @@ s_var   **rrdcgiReadVariables(
     s_var   **result;
     int       i, k, len;
     char      tmp[101];
+    size_t    tmplen;
 
     cp = getenv("REQUEST_METHOD");
     ip = getenv("CONTENT_LENGTH");
@@ -1330,15 +1394,15 @@ s_var   **rrdcgiReadVariables(
             length = atoi(ip);
             if ((line = (char *) malloc(length + 2)) == NULL)
                 return NULL;
-            fgets(line, length + 1, stdin);
+            if (fgets(line, length + 1, stdin) == NULL)
+                return NULL;
         } 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)
+            if ((line = strdup(esp)) == NULL)
                 return NULL;
-            sprintf(line, "%s", esp);
         } else
             return NULL;
     } else {
@@ -1346,22 +1410,18 @@ s_var   **rrdcgiReadVariables(
         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 ((tmplen = strlen(tmp)) != 0) {
+                if (tmp[tmplen - 1] == '\n')
+                    tmp[tmplen - 1] = '&';
+                length += tmplen;
+                len = (length + 1) * sizeof(char);
+                if ((unsigned) length > tmplen) {
                     if ((line = (char *) realloc(line, len)) == NULL)
                         return NULL;
-                    strcat(line, tmp);
+                    strncat(line, tmp, tmplen);
                 } else {
-                    length = strlen(tmp);
-                    len = (length + 1) * sizeof(char);
-                    if ((line = (char *) malloc(len)) == NULL)
+                    if ((line = strdup(tmp)) == NULL)
                         return NULL;
-                    memset(line, 0, len);
-                    strcpy(line, tmp);
                 }
             }
             memset(tmp, 0, sizeof(tmp));
@@ -1459,14 +1519,10 @@ s_var   **rrdcgiReadVariables(
                 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)
+                len = strlen(result[k]->value) + (ip - esp) + 2;
+                if ((sptr = (char *) calloc(len, sizeof(char))) == NULL)
                     return NULL;
-                memset(sptr, 0, len);
-                sprintf(sptr, "%s\n", result[k]->value);
-                strncat(sptr, cp, ip - esp);
+                snprintf(sptr, len, "%s\n%s", result[k]->value, cp);
                 free(result[k]->value);
                 result[k]->value = rrdcgiDecodeString(sptr);
             }