/*****************************************************************************
* RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
*****************************************************************************
* rrd_cgi.c RRD Web Page Generator
*****************************************************************************/
#include "rrd_tool.h"
#define MEMBLK 1024
/*#define DEBUG_PARSER
#define DEBUG_VARS*/
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 , const char **));
/**************************************************/
/* tag replacers ... they are called from parse */
/* through function pointers */
/**************************************************/
/* return cgi var named arg[0] */
char* cgiget(long , const char **);
/* return a quoted cgi var named arg[0] */
char* cgigetq(long , const char **);
/* return a quoted and sanitized cgi variable */
char* cgigetqp(long , const 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, const char **);
/* pretty-print the value for some.rrd via strftime() */
char* printtimelast(long, const char **);
/* pretty-print current time */
char* printtimenow(long, const char **);
/* set an environment variable */
char* rrdsetenv(long, const char **);
/* get an environment variable */
char* rrdgetenv(long, const char **);
/* include the named file at this point */
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);
/* 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()
{
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()
{
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, "= 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, 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]");
};
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){
struct rrd_time_value 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 */
parsetime("end-24h", &start_tv);
parsetime("now", &end_tv);
/* 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 stralloc("");
}
if( (parsetime_error = 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) {
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]");
}
}
/* 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 occurrences of .. this is a general measure to make
paths which came in via cgi do not go UP ... */
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);
};
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);
/* Make sure the path is relative, e.g. does not start with '/' */
p = buf2;
while ('/' == *p)
{
*p++ = '_';
}
return buf2;
}
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, const char **args){
int i,xsize, ysize;
double ymin,ymax;
for(i=0;i";
}
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();
calfree();
return err;
}
}
return NULL;
}
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])' 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 = 0;
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;
}
/* 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 > 0) {
int n;
printf("<-- arguments found [%d]\n", argc);
for (n=0; n\n");
} else {
printf("\n");
}
#endif
/* update caller's notion of the argument array and it's size */
*arguments = argv;
*argument_count = argc;
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);
}
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;
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;
}
}