X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Frrd_cgi.c;h=b2195e426e308c0ec18208399d8a16dc139b24c2;hb=81dadfe5d722bbaf4c2cfb50e2101e1a78bb8fea;hp=5a47dc2b6384c5e28fd95bdb27e2510e006750b4;hpb=61a736a2e1ad057f78cb5c2245207c9d02d86f29;p=rrdtool.git diff --git a/src/rrd_cgi.c b/src/rrd_cgi.c index 5a47dc2..b2195e4 100644 --- a/src/rrd_cgi.c +++ b/src/rrd_cgi.c @@ -1,24 +1,27 @@ /***************************************************************************** - * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2004 + * RRDtool 1.2.12 Copyright by Tobi Oetiker, 1997-2005 ***************************************************************************** * rrd_cgi.c RRD Web Page Generator *****************************************************************************/ #include "rrd_tool.h" -#include -#include #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 */ -int parse(char **, long, char *, char *(*)(long , char **)); +int parse(char **, long, char *, char *(*)(long , const char **)); /**************************************************/ /* tag replacers ... they are called from parse */ @@ -26,43 +29,46 @@ int parse(char **, long, char *, char *(*)(long , char **)); /**************************************************/ /* return cgi var named arg[0] */ -char* cgiget(long , char **); +char* cgiget(long , const char **); /* return a quoted cgi var named arg[0] */ -char* cgigetq(long , char **); +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 appropriate image tag */ -char* drawgraph(long, char **); +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 environment variable */ -char* rrdsetenv(long, char **); +char* rrdsetenv(long, const char **); /* get an environment variable */ -char* rrdgetenv(long, char **); +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 **); +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, char**); +char* printstrftime(long, const char**); /** HTTP protocol needs special format, and GMT time **/ char *http_time(time_t *); @@ -70,11 +76,69 @@ 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, char **args); -char* rrdsetvar(long argc, char **args); -char* rrdsetvarconst(long argc, char **args); +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 */ @@ -117,7 +181,7 @@ donevar() { int i; if (varheap) { - for (i=0; i %s -->\n", name, varheap[i].value); @@ -157,7 +221,7 @@ static const char* putvar(const char* name, const char* value, int is_const) { int i; - for (i=0; i < varheap_size && varheap[i].name; 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) { @@ -180,7 +244,7 @@ putvar(const char* name, const char* value, int is_const) } /* no existing variable found by that name, add it */ - if (i == varheap_size) { + 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)); @@ -220,6 +284,10 @@ rrd_expand_vars(char* buffer) parse(&buffer, i, "= 2) { char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2)); if (xyz == NULL) { @@ -383,6 +460,7 @@ char* rrdsetenv(long argc, char **args) { free(xyz); return stralloc("[ERROR: failed to do putenv]"); }; + return stralloc(""); } return stralloc("[ERROR: setenv failed because not enough " "arguments were defined]"); @@ -390,7 +468,7 @@ char* rrdsetenv(long argc, char **args) { /* rrd interface to the variable function putvar() */ char* -rrdsetvar(long argc, char **args) +rrdsetvar(long argc, const char **args) { if (argc >= 2) { @@ -407,7 +485,7 @@ rrdsetvar(long argc, char **args) /* rrd interface to the variable function putvar() */ char* -rrdsetvarconst(long argc, char **args) +rrdsetvarconst(long argc, const char **args) { if (argc >= 2) { @@ -422,7 +500,7 @@ rrdsetvarconst(long argc, char **args) "were defined]"); } -char* rrdgetenv(long argc, char **args) { +char* rrdgetenv(long argc, const char **args) { char buf[128]; const char* envvar; if (argc != 1) { @@ -433,12 +511,12 @@ char* rrdgetenv(long argc, char **args) { if (envvar) { return stralloc(envvar); } else { - snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]); - return stralloc(buf); + snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]); + return stralloc(buf); } } -char* rrdgetvar(long argc, char **args) { +char* rrdgetvar(long argc, const char **args) { char buf[128]; const char* value; if (argc != 1) { @@ -449,12 +527,12 @@ char* rrdgetvar(long argc, char **args) { if (value) { return stralloc(value); } else { - snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]); + snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]); return stralloc(buf); } } -char* rrdgoodfor(long argc, char **args){ +char* rrdgoodfor(long argc, const char **args){ if (argc == 1) { goodfor = atol(args[0]); } else { @@ -468,11 +546,29 @@ char* rrdgoodfor(long argc, 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], "COPYRIGHT") == 0) { + return stralloc(PACKAGE_COPYRIGHT); + } else if( strcasecmp( args[0], "COMPILETIME") == 0) { + return stralloc(__DATE__ " " __TIME__); + } else if( strcasecmp( args[0], "OS") == 0) { + return stralloc(OS); + } 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, char **args){ +char* printstrftime(long argc, const char **args){ struct rrd_time_value start_tv, end_tv; char *parsetime_error = NULL; char formatted[MAX_STRFTIME_SIZE]; @@ -524,10 +620,10 @@ char* printstrftime(long argc, char **args){ } } -char* includefile(long argc, char **args){ +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)); @@ -566,9 +662,9 @@ char* rrdstrip(char *buf) { return buf; } -char* cgigetq(long argc, char **args){ +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; @@ -604,80 +700,82 @@ char* cgigetq(long argc, char **args){ /* 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) + 4 * qc + 4))) == 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++); - } - } + 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++) = '"'; - *(d) = '\0'; + + *d = 0; free(buf); + + /* Make sure the path is relative, e.g. does not start with '/' */ + p = buf2; + while ('/' == *p) + { + *p++ = '_'; + } + return buf2; - } - return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]"); } -char* cgiget(long argc, 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"; } - optind=0; /* reset gnu getopt */ - opterr=0; /* reset gnu getopt */ calfree(); - if( rrd_graph(argc+1, args-1, &calcpr, &xsize, &ysize,NULL) != -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()) { @@ -691,7 +789,7 @@ char* drawgraph(long argc, char **args){ return NULL; } -char* drawprint(long argc, char **args){ +char* drawprint(long argc, const char **args){ if (argc==1 && calcpr){ long i=0; while (calcpr[i] != NULL) i++; /*determine number lines in calcpr*/ @@ -701,7 +799,7 @@ char* drawprint(long argc, char **args){ return stralloc("[ERROR: RRD::PRINT argument error]"); } -char* printtimelast(long argc, char **args) { +char* printtimelast(long argc, const char **args) { time_t last; struct tm tm_last; char *buf; @@ -710,7 +808,7 @@ char* printtimelast(long argc, 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()); @@ -727,7 +825,7 @@ char* printtimelast(long argc, char **args) { return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]"); } -char* printtimenow(long argc, char **args) { +char* printtimenow(long argc, const char **args) { time_t now = time(NULL); struct tm tm_now; char *buf; @@ -817,7 +915,7 @@ scanargs(char *line, int *argument_count, char ***arguments) { case ' ': if (Quote || tagcount) { - /* copy quoted/tagged string */ + /* copy quoted/tagged (=RRD expanded) string */ *putP++ = c; } else if (in_arg) @@ -843,7 +941,7 @@ scanargs(char *line, int *argument_count, char ***arguments) } } else { if (!in_arg) { - /* reference argument string in argument array */ + /* reference start of argument string in argument array */ argv[argc++] = putP; in_arg=1; } @@ -852,7 +950,6 @@ scanargs(char *line, int *argument_count, char ***arguments) break; default: - if (!Quote) { if (!in_arg) { /* start new argument */ argv[argc++] = putP; @@ -869,7 +966,6 @@ scanargs(char *line, int *argument_count, char ***arguments) curarg_contains_rrd_directives = 1; } } - } *putP++ = c; break; } @@ -929,7 +1025,7 @@ parse( char **buf, /* buffer */ long i, /* offset in buffer */ char *tag, /* tag to handle */ - char *(*func)(long argc, char **args) /* function to call for 'tag' */ + char *(*func)(long , const char **) /* function to call for 'tag' */ ) { /* the name of the vairable ... */ @@ -961,7 +1057,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 @@ -1028,3 +1124,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 ("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 (iname,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 ("

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; +} + +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; + } +} +