X-Git-Url: https://git.octo.it/?p=rrdtool.git;a=blobdiff_plain;f=src%2Frrd_cgi.c;h=1ffe8fd4639559668a2798d2bc208b42985cb14c;hp=f6a5908c4ec5f5ae695057bbc7f6f13fe4e255a4;hb=f8a299184c27cbc7f15d7ce9172bf89f121f0705;hpb=b5080d2d2d0f82f4e9b2e0b0745f8f328edec3b9 diff --git a/src/rrd_cgi.c b/src/rrd_cgi.c index f6a5908..1ffe8fd 100644 --- a/src/rrd_cgi.c +++ b/src/rrd_cgi.c @@ -1,585 +1,1641 @@ /***************************************************************************** - * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002 + * RRDtool 1.4.3 Copyright by Tobi Oetiker, 1997-2010 ***************************************************************************** * rrd_cgi.c RRD Web Page Generator *****************************************************************************/ #include "rrd_tool.h" -#include -#include +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef WIN32 + #define strcasecmp stricmp + #define strcasencmp strnicmp +#endif #define MEMBLK 1024 +/*#define DEBUG_PARSER +#define DEBUG_VARS*/ + +typedef struct var_s { + char *name, *value; +} s_var; -/* global variable for libcgi */ -s_cgi **cgiArg; +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 */ -int parse(char **, long, char *, char *(*)(long , char **)); +int parse( + char **, + long, + char *, + char * (*)(long, + const char **)); /**************************************************/ /* tag replacers ... they are called from parse */ /* through function pointers */ /**************************************************/ -/* return cgi var named arg[0] */ -char* cgiget(long , char **); +/* return cgi var named arg[0] */ +char *cgiget( + long, + const char **); -/* return a quoted cgi var named arg[0] */ -char* cgigetq(long , char **); +/* return a quoted cgi var named arg[0] */ +char *cgigetq( + long, + const char **); /* return a quoted and sanitized cgi variable */ -char* cgigetqp(long , char **); +char *cgigetqp( + long, + const char **); -/* call rrd_graph and insert apropriate image tag */ -char* drawgraph(long, char **); +/* call rrd_graph and insert appropriate image tag */ +char *drawgraph( + long, + const char **); /* return PRINT functions from last rrd_graph call */ -char* drawprint(long, char **); +char *drawprint( + long, + const char **); /* pretty-print the value for some.rrd via strftime() */ -char* printtimelast(long, char **); +char *printtimelast( + long, + const char **); /* pretty-print current time */ -char* printtimenow(long,char **); +char *printtimenow( + long, + const char **); -/* set an evironment variable */ -char* rrdsetenv(long, char **); +/* set an environment variable */ +char *rrdsetenv( + long, + const char **); -/* get an evironment variable */ -char* rrdgetenv(long, char **); +/* get an environment variable */ +char *rrdgetenv( + long, + const char **); /* include the named file at this point */ -char* includefile(long, char **); +char *includefile( + long, + const char **); /* for how long is the output of the cgi valid ? */ -char* rrdgoodfor(long, char **); - -/** http protocol needs special format, and GMT time **/ -char *http_time(time_t *); - -/* return a pointer to newly alocated copy of this string */ -char *stralloc(char *); - -static long goodfor=0; -static char **calcpr=NULL; -static void calfree (void){ - if (calcpr) { - long i; - for(i=0;calcpr[i];i++){ - if (calcpr[i]){ - free(calcpr[i]); - } - } +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); + +/* format at-time specified times using strftime */ +char *printstrftime( + long, + const char **); + +/** HTTP protocol needs special format, and GMT time **/ +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); +char *rrdsetvar( + long argc, + const char **args); +char *rrdsetvarconst( + long argc, + const char **args); + + +/* variable store: put/get key-value pairs */ +static int initvar( + ); +static void donevar( + ); +static const char *getvar( + const char *varname); +static const char *putvar( + const char *name, + const char *value, + int is_const); + +/* key value pair that makes up an entry in the variable store */ +typedef struct { + int is_const; /* const variable or not */ + const char *name; /* variable name */ + const char *value; /* variable value */ +} vardata; + +/* the variable heap: + start with a heapsize of 10 variables */ +#define INIT_VARSTORE_SIZE 10 +static vardata *varheap = NULL; +static size_t varheap_size = 0; + +/* allocate and initialize variable heap */ +static int initvar( + void) +{ + varheap = (vardata *) malloc(sizeof(vardata) * INIT_VARSTORE_SIZE); + if (varheap == NULL) { + fprintf(stderr, "ERROR: unable to initialize variable store\n"); + return -1; + } + memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE); + varheap_size = INIT_VARSTORE_SIZE; + return 0; +} + +/* cleanup: free allocated memory */ +static void donevar( + void) +{ + int i; + + if (varheap) { + for (i = 0; i < (int) varheap_size; i++) { + if (varheap[i].name) { + free((char *) varheap[i].name); + } + if (varheap[i].value) { + free((char *) varheap[i].value); + } + } + free(varheap); + } +} + +/* Get a variable from the variable store. + Return NULL in case the requested variable was not found. */ +static const char *getvar( + const char *name) +{ + int i; + + for (i = 0; i < (int) varheap_size && varheap[i].name; i++) { + if (0 == strcmp(name, varheap[i].name)) { +#ifdef DEBUG_VARS + printf("\n", name, varheap[i].value); +#endif + return varheap[i].value; + } + } +#ifdef DEBUG_VARS + printf("\n", name); +#endif + return NULL; +} + +/* Put a variable into the variable store. If a variable by that + name exists, it's value is overwritten with the new value unless it was + marked as 'const' (initialized by RRD::SETCONSTVAR). + Returns a copy the newly allocated value on success, NULL on error. */ +static const char *putvar( + const char *name, + const char *value, + int is_const) +{ + int i; + + for (i = 0; i < (int) varheap_size && varheap[i].name; i++) { + if (0 == strcmp(name, varheap[i].name)) { + /* overwrite existing entry */ + if (varheap[i].is_const) { +#ifdef DEBUG_VARS + printf("\n", name, value); +#endif + return varheap[i].value; + } +#ifdef DEBUG_VARS + printf("\n", + name, value, varheap[i].value); +#endif + /* make it possible to promote a variable to readonly */ + varheap[i].is_const = is_const; + free((char *) varheap[i].value); + varheap[i].value = stralloc(value); + return varheap[i].value; + } + } + + /* no existing variable found by that name, add it */ + if (i == (int) varheap_size) { + /* ran out of heap: resize heap to double size */ + size_t new_size = varheap_size * 2; + + varheap = (vardata *) (realloc(varheap, sizeof(vardata) * new_size)); + if (!varheap) { + fprintf(stderr, "ERROR: Unable to realloc variable heap\n"); + return NULL; + } + /* initialize newly allocated memory */ ; + memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size); + varheap_size = new_size; + } + varheap[i].is_const = is_const; + varheap[i].name = stralloc(name); + varheap[i].value = stralloc(value); + +#ifdef DEBUG_VARS + printf("\n", name, value); +#endif + return varheap[i].value; +} + +/* expand those RRD:* directives that can be used recursivly */ +static char *rrd_expand_vars( + char *buffer) +{ + int i; + +#ifdef DEBUG_PARSER + printf("expanding variables in '%s'\n", buffer); +#endif + + for (i = 0; buffer[i]; i++) { + if (buffer[i] != '<') + continue; + parse(&buffer, i, "= 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( + int argc, + char *argv[]) +{ + long length; + char *buffer; + char *server_url = NULL; + long i; + long filter = 0; + struct option long_options[] = { + {"filter", no_argument, 0, 'f'}, + {0, 0, 0, 0} + }; + #ifdef MUST_DISABLE_SIGFPE - signal(SIGFPE,SIG_IGN); + signal(SIGFPE, SIG_IGN); #endif #ifdef MUST_DISABLE_FPMASK - fpsetmask(0); + fpsetmask(0); #endif - /* what do we get for cmdline arguments? - for (i=0;i 0){ - time_t now; - now = time(NULL); - printf ("Last-Modified: %s\n",http_time(&now)); - now += labs(goodfor); - printf ("Expires: %s\n",http_time(&now)); - if (goodfor < 0) { - printf("Refresh: %ld\n", labs(goodfor)); - } - } - printf ("\n"); - } - printf ("%s", buffer); - calfree(); - if (buffer){ - free(buffer); - } - exit(0); -} - -/* remove occurences of .. this is a general measure to make + } + } + + if (!filter) { + rrdcgiDebug(0, 0); + rrdcgiArg = rrdcgiInit(); + server_url = getenv("SERVER_URL"); + } + + /* make sure we have one extra argument, + if there are others, we do not care Apache gives several */ + + /* if ( (optind != argc-2 + && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL) + && optind != argc-1) { */ + + if (optind >= argc) { + fprintf(stderr, "ERROR: expected a filename\n"); + exit(1); + } else { + length = readfile(argv[optind], &buffer, 1); + } + + if (rrd_test_error()) { + fprintf(stderr, "ERROR: %s\n", rrd_get_error()); + exit(1); + } + + /* initialize variable heap */ + initvar(); + +#ifdef DEBUG_PARSER + /* some fake header for testing */ + printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n"); +#endif + + + /* expand rrd directives in buffer recursivly */ + for (i = 0; buffer[i]; i++) { + if (buffer[i] != '<') + continue; + if (!filter) { + parse(&buffer, i, " 0) { + time_t now; + + now = time(NULL); + printf("Last-Modified: %s\n", http_time(&now)); + now += labs(goodfor); + printf("Expires: %s\n", http_time(&now)); + if (goodfor < 0) { + printf("Refresh: %ld\n", labs(goodfor)); + } + } + printf("\n"); + } + + /* output result */ + printf("%s", buffer); + + /* cleanup */ + calfree(); + if (buffer) { + free(buffer); + } + donevar(); + exit(0); +} + +/* remove occurrences of .. this is a general measure to make paths which came in via cgi do not go UP ... */ -char* rrdsetenv(long argc, char **args){ - if (argc >= 2) { - char *xyz=malloc((strlen(args[0])+strlen(args[1])+3)*sizeof(char)); - if (xyz == NULL){ - return stralloc("[ERROR: allocating setenv buffer]"); - }; - sprintf(xyz,"%s=%s",args[0],args[1]); - if( putenv(xyz) == -1) { - return stralloc("[ERROR: faild to do putenv]"); - }; - } else { - return stralloc("[ERROR: setenv faild because not enough arguments were defined]"); - } - return stralloc(""); -} - -char* rrdgetenv(long argc, char **args){ - if (argc != 1) { - return stralloc("[ERROR: getenv faild because it did not get 1 argument only]"); - } - else if (getenv(args[0]) == NULL) { - return stralloc(""); - } - else { - return stralloc(getenv(args[0])); - } -} - -char* rrdgoodfor(long argc, char **args){ - if (argc == 1) { - goodfor = atol(args[0]); - } else { - return stralloc("[ERROR: goodfor expected 1 argument]"); - } - - if (goodfor == 0){ - return stralloc("[ERROR: goodfor value must not be 0]"); - } - - return stralloc(""); -} - -char* includefile(long argc, char **args){ - char *buffer; - if (argc >= 1) { - readfile(args[0], &buffer, 0); - if (rrd_test_error()) { - char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char)); - sprintf(err, "[ERROR: %s]",rrd_get_error()); - rrd_clear_error(); - return err; - } else { - return buffer; - } - } - else - { - return stralloc("[ERROR: No Inclue file defined]"); - } -} - -char* rrdstrip(char *buf){ - char *start; - if (buf == NULL) return NULL; - buf = stralloc(buf); /* make a copy of the buffer */ - if (buf == NULL) return NULL; - while ((start = strstr(buf,"<"))){ - *start = '_'; - } - while ((start = strstr(buf,">"))){ - *start = '_'; - } - return buf; -} - -char* cgigetq(long argc, char **args){ - if (argc>= 1){ - char *buf = rrdstrip(cgiGetValue(cgiArg,args[0])); - char *buf2; - char *c,*d; - int qc=0; - if (buf==NULL) return NULL; - - for(c=buf;*c != '\0';c++) - if (*c == '"') qc++; - if((buf2=malloc((strlen(buf) + qc*4 +4) * sizeof(char)))==NULL){ - perror("Malloc Buffer"); - exit(1); +char *rrdsetenv( + long argc, + const char **args) +{ + if (argc >= 2) { + char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2)); + + if (xyz == NULL) { + return stralloc("[ERROR: allocating setenv buffer]"); + }; + sprintf(xyz, "%s=%s", args[0], args[1]); + if (putenv(xyz) == -1) { + free(xyz); + return stralloc("[ERROR: failed to do putenv]"); + }; + return stralloc(""); + } + return stralloc("[ERROR: setenv failed because not enough " + "arguments were defined]"); +} + +/* rrd interface to the variable function putvar() */ +char *rrdsetvar( + long argc, + const char **args) +{ + if (argc >= 2) { + const char *result = putvar(args[0], args[1], 0 /* not const */ ); + + if (result) { + /* setvar does not return the value set */ + return stralloc(""); + } + return stralloc("[ERROR: putvar failed]"); + } + return stralloc("[ERROR: putvar failed because not enough arguments " + "were defined]"); +} + +/* rrd interface to the variable function putvar() */ +char *rrdsetvarconst( + long argc, + const char **args) +{ + if (argc >= 2) { + const char *result = putvar(args[0], args[1], 1 /* const */ ); + + if (result) { + /* setvar does not return the value set */ + return stralloc(""); + } + return stralloc("[ERROR: putvar failed]"); + } + return stralloc("[ERROR: putvar failed because not enough arguments " + "were defined]"); +} + +char *rrdgetenv( + long argc, + const char **args) +{ + char buf[128]; + const char *envvar; + + if (argc != 1) { + return stralloc("[ERROR: getenv failed because it did not " + "get 1 argument only]"); }; - c=buf; - d=buf2; - *(d++) = '"'; - while(*c != '\0'){ - if (*c == '"') { - *(d++) = '"'; - *(d++) = '\''; - *(d++) = '"'; - *(d++) = '\''; - } - *(d++) = *(c++); - } - *(d++) = '"'; - *(d) = '\0'; - free(buf); - return buf2; - } + envvar = getenv(args[0]); + if (envvar) { + return stralloc(envvar); + } else { + snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]); + return stralloc(buf); + } +} + +char *rrdgetvar( + long argc, + const char **args) +{ + char buf[128]; + const char *value; + + if (argc != 1) { + return stralloc("[ERROR: getvar failed because it did not " + "get 1 argument only]"); + }; + value = getvar(args[0]); + if (value) { + return stralloc(value); + } else { + snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]); + return stralloc(buf); + } +} + +char *rrdgoodfor( + long argc, + const char **args) +{ + if (argc == 1) { + goodfor = atol(args[0]); + } else { + return stralloc("[ERROR: goodfor expected 1 argument]"); + } + + if (goodfor == 0) { + return stralloc("[ERROR: goodfor value must not be 0]"); + } + + 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. + * */ +#define MAX_STRFTIME_SIZE 256 +char *printstrftime( + long argc, + const char **args) +{ + rrd_time_value_t start_tv, end_tv; + char *parsetime_error = NULL; + char formatted[MAX_STRFTIME_SIZE]; + struct tm *the_tm; + time_t start_tmp, end_tmp; + + /* Make sure that we were given the right number of args */ + if (argc != 4) { + rrd_set_error("wrong number of args %d", argc); + return stralloc(""); + } + + /* Init start and end time */ + rrd_parsetime("end-24h", &start_tv); + rrd_parsetime("now", &end_tv); + + /* Parse the start and end times we were given */ + if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) { + rrd_set_error("start time: %s", parsetime_error); + return stralloc(""); + } + if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) { + rrd_set_error("end time: %s", parsetime_error); + return stralloc(""); + } + if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) { + return stralloc(""); + } + + /* Do we do the start or end */ + if (strcasecmp(args[0], "START") == 0) { + the_tm = localtime(&start_tmp); + } else if (strcasecmp(args[0], "END") == 0) { + the_tm = localtime(&end_tmp); + } else { + rrd_set_error("start/end not found in '%s'", args[0]); + return stralloc(""); + } + + /* now format it */ + if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) { + return (stralloc(formatted)); + } else { + rrd_set_error("strftime failed"); + return stralloc(""); + } +} + +char *includefile( + long argc, + const char **args) +{ + char *buffer; + + if (argc >= 1) { + const char *filename = args[0]; + + readfile(filename, &buffer, 0); + if (rrd_test_error()) { + char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE)); + + sprintf(err, "[ERROR: %s]", rrd_get_error()); + rrd_clear_error(); + return err; + } else { + return buffer; + } + } else { + return stralloc("[ERROR: No Inclue file defined]"); + } +} - return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]"); +/* make a copy of buf and replace open/close brackets with '_' */ +char *rrdstrip( + char *buf) +{ + char *p; + + if (buf == NULL) { + return NULL; + } + /* make a copy of the buffer */ + buf = stralloc(buf); + if (buf == NULL) { + return NULL; + } + + p = buf; + while (*p) { + if (*p == '<' || *p == '>') { + *p = '_'; + } + p++; + } + return buf; +} + +char *cgigetq( + long argc, + const char **args) +{ + if (argc >= 1) { + char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0])); + char *buf2; + char *c, *d; + int qc = 0; + + if (buf == NULL) + return NULL; + + for (c = buf; *c != '\0'; c++) + if (*c == '"') + qc++; + if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) { + perror("Malloc Buffer"); + exit(1); + }; + c = buf; + d = buf2; + *(d++) = '"'; + while (*c != '\0') { + if (*c == '"') { + *(d++) = '"'; + *(d++) = '\''; + *(d++) = '"'; + *(d++) = '\''; + } + *(d++) = *(c++); + } + *(d++) = '"'; + *(d) = '\0'; + free(buf); + return buf2; + } + + return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]"); } -/* remove occurences of .. this is a general measure to make +/* remove occurrences of .. this is a general measure to make paths which came in via cgi do not go UP ... */ -char* cgigetqp(long argc, char **args){ - if (argc>= 1){ - char *buf = rrdstrip(cgiGetValue(cgiArg,args[0])); - char *buf2; - char *c,*d; - int qc=0; - if (buf==NULL) return NULL; - - for(c=buf;*c != '\0';c++) - if (*c == '"') qc++; - if((buf2=malloc((strlen(buf) + qc*4 +4) * sizeof(char)))==NULL){ - perror("Malloc Buffer"); - exit(1); +char *cgigetqp( + long argc, + const char **args) +{ + char *buf; + char *buf2; + char *p; + char *d; + + if (argc < 1) { + return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]"); + } + + buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0])); + if (!buf) { + return NULL; + } + + buf2 = malloc(strlen(buf) + 1); + if (!buf2) { + perror("cgigetqp(): Malloc Path Buffer"); + exit(1); }; - c=buf; - d=buf2; - *(d++) = '"'; - while(*c != '\0'){ - if (*c == '"') { - *(d++) = '"'; - *(d++) = '\''; - *(d++) = '"'; - *(d++) = '\''; - } - if(*c == '/') { - *(d++) = '_';c++; - } else { - if (*c=='.' && *(c+1) == '.'){ - c += 2; - *(d++) = '_'; *(d++) ='_'; - } else { - - *(d++) = *(c++); - } - } - } - *(d++) = '"'; - *(d) = '\0'; + + p = buf; + d = buf2; + + while (*p) { + /* prevent mallicious paths from entering the system */ + if (p[0] == '.' && p[1] == '.') { + p += 2; + *d++ = '_'; + *d++ = '_'; + } else { + *d++ = *p++; + } + } + + *d = 0; free(buf); - return buf2; - } - return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]"); + /* Make sure the path is relative, e.g. does not start with '/' */ + p = buf2; + while ('/' == *p) { + *p++ = '_'; + } + return buf2; } -char* cgiget(long argc, char **args){ - if (argc>= 1) - return rrdstrip(cgiGetValue(cgiArg,args[0])); - else - return stralloc("[ERROR: not enough arguments for RRD::CV]"); +char *cgiget( + long argc, + const char **args) +{ + if (argc >= 1) + return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0])); + else + return stralloc("[ERROR: not enough arguments for RRD::CV]"); } -char* drawgraph(long argc, char **args){ - int i,xsize, ysize; - for(i=0;i"; - } - optind=0; /* reset gnu getopt */ - opterr=0; /* reset gnu getopt */ - calfree(); - if( rrd_graph(argc+1, args-1, &calcpr, &xsize, &ysize) != -1 ) { - 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()); - rrd_clear_error(); - calfree(); - return err; - } - } - return NULL; -} - -char* drawprint(long argc, char **args){ - if (argc==1 && calcpr){ - long i=0; - while (calcpr[i] != NULL) i++; /*determine number lines in calcpr*/ - if (atol(args[0])"; + } + calfree(); + if (rrd_graph + (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin, + &ymax) != -1) { + 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()); + rrd_clear_error(); + return err; + } } - tm_last = *localtime(&last); - 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]"); -} - -char* printtimenow(long argc, char **args) { - time_t now = time(NULL); - struct tm tm_now; - char *buf; - if ( argc == 1 ) { - buf = malloc(255); - if (buf == NULL){ - return stralloc("[ERROR: allocating strftime buffer]"); - }; - tm_now = *localtime(&now); - strftime(buf,254,args[0],&tm_now); - return buf; - } - if ( argc < 1 ) { - return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]"); - } - return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]"); -} - -/* scan aLine until an unescaped '>' arives */ -char* scanargs(char *aLine, long *argc, char ***args) -{ - char *getP, *putP; - char Quote = 0; - int argal = MEMBLK; - int braket = 0; - int inArg = 0; - if (((*args) = (char **) malloc(MEMBLK*sizeof(char *))) == NULL) { return NULL; - } - /* sikp leading blanks */ - while (*aLine && *aLine <= ' ') aLine++; - - *argc = 0; - getP = aLine; - putP = aLine; - while (*getP && !( !Quote && (braket == 0) && ((*getP) == '>'))){ - if ((unsigned)*getP < ' ') *getP = ' '; /*remove all special chars*/ - switch (*getP) { - case ' ': - if (Quote){ - *(putP++)=*getP; - } else - if(inArg) { - *(putP++) = 0; - inArg = 0; - } - break; - case '"': - case '\'': - if (Quote != 0) { - if (Quote == *getP) - Quote = 0; - else { - *(putP++)=*getP; - } - } else { - if(!inArg){ - (*args)[++(*argc)] = putP; - inArg=1; - } - Quote = *getP; - } - break; - default: - if (Quote == 0 && (*getP) == '<') { - braket++; - } - if (Quote == 0 && (*getP) == '>') { - braket--; - } - - if(!inArg){ - (*args)[++(*argc)] = putP; - inArg=1; - } - *(putP++)=*getP; - break; - } - if ((*argc) >= argal-10 ) { - argal += MEMBLK; - if (((*args)=rrd_realloc((*args),(argal)*sizeof(char *))) == NULL) { - return NULL; - } - } - getP++; - } - - *putP = '\0'; - (*argc)++; - if (Quote) +} + +char *drawprint( + long argc, + const char **args) +{ + if (argc == 1 && calcpr) { + long i = 0; + + while (calcpr[i] != NULL) + i++; /*determine number lines in calcpr */ + if (atol(args[0]) < i - 1) + return stralloc(calcpr[atol(args[0]) + 1]); + } + return stralloc("[ERROR: RRD::PRINT argument error]"); +} + +char *printtimelast( + long argc, + const char **args) +{ + time_t last; + struct tm tm_last; + char *buf; + + if (argc == 2) { + buf = malloc(255); + if (buf == NULL) { + return stralloc("[ERROR: allocating strftime buffer]"); + }; + /* 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()); + rrd_clear_error(); + return err; + } + tm_last = *localtime(&last); + strftime(buf, 254, args[1], &tm_last); + return buf; + } + return stralloc("[ERROR: expected ]"); +} + +char *printtimenow( + long argc, + const char **args) +{ + time_t now = time(NULL); + struct tm tm_now; + char *buf; + + if (argc == 1) { + buf = malloc(255); + if (buf == NULL) { + return stralloc("[ERROR: allocating strftime buffer]"); + }; + tm_now = *localtime(&now); + strftime(buf, 254, args[0], &tm_now); + return buf; + } + if (argc < 1) { + return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]"); + } + return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]"); +} + +/* Scan buffer until an unescaped '>' arives. + * Update argument array with arguments found. + * Return end cursor where parsing stopped, or NULL in case of failure. + * + * FIXME: + * To allow nested constructs, we call rrd_expand_vars() for arguments + * that contain RRD::x directives. These introduce a small memory leak + * since we have to stralloc the arguments the way parse() works. + */ +char *scanargs( + char *line, + int *argument_count, + char ***arguments) +{ + char *getP; /* read cursor */ + char *putP; /* write cursor */ + char Quote; /* type of quote if in quoted string, 0 otherwise */ + int tagcount; /* open tag count */ + int in_arg; /* if we currently are parsing an argument or not */ + int argsz; /* argument array size */ + int curarg_contains_rrd_directives; + + /* local array of arguments while parsing */ + int argc = 1; + char **argv; + +#ifdef DEBUG_PARSER + printf("<-- scanargs(%s) -->\n", line); +#endif + + *arguments = NULL; + *argument_count = 0; + + /* create initial argument array of char pointers */ + argsz = 32; + argv = (char **) malloc(argsz * sizeof(char *)); + if (!argv) { + return NULL; + } + argv[0] = "rrdcgi"; + + /* skip leading blanks */ + while (isspace((int) *line)) { + line++; + } + + getP = line; + putP = line; + + Quote = 0; + in_arg = 0; + tagcount = 0; + + curarg_contains_rrd_directives = 0; + + /* start parsing 'line' for arguments */ + while (*getP) { + unsigned char c = *getP++; + + if (c == '>' && !Quote && !tagcount) { + /* this is our closing tag, quit scanning */ + break; + } + + /* remove all special chars */ + if (c < ' ') { + c = ' '; + } + + switch (c) { + case ' ': + if (Quote || tagcount) { + /* copy quoted/tagged (=RRD expanded) string */ + *putP++ = c; + } else if (in_arg) { + /* end argument string */ + *putP++ = 0; + in_arg = 0; + if (curarg_contains_rrd_directives) { + argv[argc - 1] = + rrd_expand_vars(stralloc(argv[argc - 1])); + curarg_contains_rrd_directives = 0; + } + } + break; + + case '"': /* Fall through */ + case '\'': + if (Quote != 0) { + if (Quote == c) { + Quote = 0; + } else { + /* copy quoted string */ + *putP++ = c; + } + } else { + if (!in_arg) { + /* reference start of argument string in argument array */ + argv[argc++] = putP; + in_arg = 1; + } + Quote = c; + } + break; + + default: + if (!in_arg) { + /* start new argument */ + argv[argc++] = putP; + in_arg = 1; + } + if (c == '>') { + if (tagcount) { + tagcount--; + } + } + if (c == '<') { + tagcount++; + if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) { + curarg_contains_rrd_directives = 1; + } + } + *putP++ = c; + break; + } + + /* check if our argument array is still large enough */ + if (argc == argsz) { + /* resize argument array */ + argsz *= 2; + argv = rrd_realloc(argv, argsz * sizeof(char *)); + if (*argv == NULL) { + return NULL; + } + } + } + + /* terminate last argument found */ + *putP = '\0'; + if (curarg_contains_rrd_directives) { + argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1])); + } +#ifdef DEBUG_PARSER + if (argc > 1) { + int n; + + printf("<-- arguments found [%d]\n", argc); + for (n = 0; n < argc; n++) { + printf("arg %02d: '%s'\n", n, argv[n]); + } + printf("-->\n"); + } else { + printf("\n"); + } +#endif + + /* update caller's notion of the argument array and it's size */ + + /* 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; + } + + /* Return new scanning cursor: + pointer to char after closing bracket */ + return getP; +} + + +/* + * Parse(): scan current portion of buffer for given tag. + * If found, parse tag arguments and call 'func' for it. + * The result of func is inserted at the current position + * in the buffer. + */ +int parse( + char **buf, /* buffer */ + long i, /* offset in buffer */ + char *tag, /* tag to handle */ + char * (*func) (long, + const char **) /* function to call for 'tag' */ + ) +{ + /* the name of the vairable ... */ + char *val; + long valln; + char **args; + char *end; + long end_offset; + int argc; + size_t taglen = strlen(tag); + + /* Current position in buffer should start with 'tag' */ + if (strncmp((*buf) + i, tag, taglen) != 0) { + return 0; + } + /* .. and match exactly (a whitespace following 'tag') */ + if (!isspace(*((*buf) + i + taglen))) { + return 0; + } +#ifdef DEBUG_PARSER + printf("parse(): handling tag '%s'\n", tag); +#endif + + /* Scan for arguments following the tag; + scanargs() puts \0 into *buf ... so after scanargs it is probably + not a good time to use strlen on buf */ + end = scanargs((*buf) + i + taglen, &argc, &args); + if (end) { + /* got arguments, call function for 'tag' with arguments */ + val = func(argc, (const char **) 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; + + val = stralloc("[ERROR: Parsing Problem with the following text\n" + " Check original file. This may have been altered " + "by parsing.]\n\n"); + } + + /* remember offset where we have to continue parsing */ + end_offset = end - (*buf); + + valln = 0; + if (val) { + valln = strlen(val); + } + + /* Optionally resize buffer to hold the replacement value: + Calculating the new length of the buffer is simple. add current + buffer pos (i) to length of string after replaced tag to length + of replacement string and add 1 for the final zero ... */ + if (end - (*buf) < (i + valln)) { + /* make sure we do not shrink the mallocd block */ + size_t newbufsize = i + strlen(end) + valln + 1; + + *buf = rrd_realloc(*buf, newbufsize); + + if (*buf == NULL) { + perror("Realoc buf:"); + exit(1); + }; + } + + /* Update new end pointer: + make sure the 'end' pointer gets moved along with the + buf pointer when realloc moves memory ... */ + end = (*buf) + end_offset; + + /* splice the variable: + step 1. Shift pending data to make room for 'val' */ + memmove((*buf) + i + valln, end, strlen(end) + 1); + + /* step 2. Insert val */ + if (val) { + memmove((*buf) + i, val, valln); + free(val); + } + return (valln > 0 ? valln - 1 : valln); +} + +char *http_time( + time_t *now) +{ + struct tm *tmptime; + static char buf[60]; + + tmptime = gmtime(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; + 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) + 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 + ("Received cgi input
\n
\n--\n%s\n--\n
\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.
\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) == + (size_t) (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("

Variable %s

\n
\n%s\n
\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
\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
\n", name); + } return NULL; - else - return getP+1; /* pointer to next char after parameter */ -} - - - -int parse(char **buf, long i, char *tag, - char *(*func)(long argc, char **args)){ - - /* the name of the vairable ... */ - char *val; - long valln; - char **args; - char *end; - long end_offset; - long argc; - /* do we like it ? */ - if (strncmp((*buf)+i, tag, strlen(tag))!=0) return 0; - if (! isspace(*( (*buf) + i + strlen(tag) )) ) return 0; - /* scanargs puts \0 into *buf ... so after scanargs it is probably - not a good time to use strlen on buf */ - end = scanargs((*buf)+i+strlen(tag),&argc,&args); - if (! end) { - for (;argc>2;argc--){ - *((args[argc-1])-1)=' '; - } - val = stralloc("[ERROR: Parsing Problem with the following text\n" - " Check original file. This may have been altered by parsing.]\n\n"); - end = (*buf)+i+1; - } else { - val = func(argc-1,args+1); - free (args); - } - /* for (ii=0;ii 0 ? valln-1: valln; -} - -char * -http_time(time_t *now) { - struct tm *tmptime; - static char buf[60]; - - tmptime=gmtime(now); - strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime); - return(buf); +} + +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; + } }