#include "cgi.h"
#include <errno.h>
#include <syslog.h>
typedef struct
{
const char *name;
int nvalues,
avalues;
const char **values;
} var_t;
static int form_count = 0,
form_alloc = 0;
static var_t *form_vars = NULL;
static void cgi_add_variable(const char *name, int element,
const char *value);
static int cgi_compare_variables(const var_t *v1, const var_t *v2);
static var_t *cgi_find_variable(const char *name);
static int cgi_initialize_get(void);
static int cgi_initialize_post(void);
static int cgi_initialize_string(const char *data);
static void cgi_sort_variables(void);
int
cgiInitialize(void)
{
char *method;
#ifdef DEBUG
setbuf(stdout, NULL);
puts("Content-type: text/plain\n");
#endif
method = getenv("REQUEST_METHOD");
if (method == NULL)
return (0);
if (strcasecmp(method, "GET") == 0)
return (cgi_initialize_get());
else if (strcasecmp(method, "POST") == 0)
return (cgi_initialize_post());
else
return (0);
}
int
cgiCheckVariables(const char *names)
{
char name[255],
*s;
const char *val;
int element;
if (names == NULL)
return (1);
while (*names != '\0')
{
while (*names == ' ' || *names == ',')
names ++;
for (s = name; *names != '\0' && *names != ' ' && *names != ','; s ++, names ++)
*s = *names;
*s = 0;
if (name[0] == '\0')
break;
if ((s = strrchr(name, '-')) != NULL)
{
*s = '\0';
element = atoi(s + 1) - 1;
val = cgiGetArray(name, element);
}
else
val = cgiGetVariable(name);
if (val == NULL)
return (0);
if (*val == '\0')
return (0);
}
return (1);
}
const char *
cgiGetArray(const char *name,
int element)
{
var_t *var;
if ((var = cgi_find_variable(name)) == NULL)
return (NULL);
if (var->nvalues == 1)
return (var->values[0]);
if (element < 0 || element >= var->nvalues)
return (NULL);
return (var->values[element]);
}
int
cgiGetSize(const char *name)
{
var_t *var;
if ((var = cgi_find_variable(name)) == NULL)
return (0);
return (var->nvalues);
}
const char *
cgiGetVariable(const char *name)
{
const var_t *var;
var = cgi_find_variable(name);
#ifdef DEBUG
if (var == NULL)
printf("cgiGetVariable(\"%s\") is returning NULL...\n", name);
else
printf("cgiGetVariable(\"%s\") is returning \"%s\"...\n", name,
var->values[var->nvalues - 1]);
#endif
return ((var == NULL) ? NULL : var->values[var->nvalues - 1]);
}
void
cgiSetArray(const char *name,
int element,
const char *value)
{
int i;
var_t *var;
if (name == NULL || value == NULL || element < 0)
return;
if ((var = cgi_find_variable(name)) == NULL)
{
cgi_add_variable(name, element, value);
cgi_sort_variables();
}
else
{
if (element >= var->avalues)
{
var->avalues = element + 16;
var->values = (const char **)realloc((void *)(var->values),
sizeof(char *) * var->avalues);
}
if (element >= var->nvalues)
{
for (i = var->nvalues; i < element; i ++)
var->values[i] = NULL;
var->nvalues = element + 1;
}
else if (var->values[element])
free((char *)var->values[element]);
var->values[element] = strdup(value);
}
}
void
cgiSetSize(const char *name,
int size)
{
int i;
var_t *var;
if (name == NULL || size < 0)
return;
if ((var = cgi_find_variable(name)) == NULL)
return;
if (size >= var->avalues)
{
var->avalues = size + 16;
var->values = (const char **)realloc((void *)(var->values),
sizeof(char *) * var->avalues);
}
if (size > var->nvalues)
{
for (i = var->nvalues; i < size; i ++)
var->values[i] = NULL;
}
else if (size < var->nvalues)
{
for (i = size; i < var->nvalues; i ++)
if (var->values[i])
free((void *)(var->values[i]));
}
var->nvalues = size;
}
void
cgiSetVariable(const char *name,
const char *value)
{
int i;
var_t *var;
if (name == NULL || value == NULL)
return;
if ((var = cgi_find_variable(name)) == NULL)
{
cgi_add_variable(name, 0, value);
cgi_sort_variables();
}
else
{
for (i = 0; i < var->nvalues; i ++)
if (var->values[i])
free((char *)var->values[i]);
var->values[0] = strdup(value);
var->nvalues = 1;
}
}
static void
cgi_add_variable(const char *name,
int element,
const char *value)
{
var_t *var;
if (name == NULL || value == NULL)
return;
#ifdef DEBUG
printf("Adding variable \'%s\' with value \'%s\'...\n", name, value);
#endif
if (form_count >= form_alloc)
{
if (form_alloc == 0)
form_vars = malloc(sizeof(var_t) * 16);
else
form_vars = realloc(form_vars, (form_alloc + 16) * sizeof(var_t));
form_alloc += 16;
}
var = form_vars + form_count;
var->name = strdup(name);
var->nvalues = element + 1;
var->avalues = element + 1;
var->values = calloc(element + 1, sizeof(char *));
var->values[element] = strdup(value);
form_count ++;
}
static int
cgi_compare_variables(const var_t *v1,
const var_t *v2)
{
return (strcasecmp(v1->name, v2->name));
}
static var_t *
cgi_find_variable(const char *name)
{
var_t key;
if (form_count < 1 || name == NULL)
return (NULL);
key.name = name;
return ((var_t *)bsearch(&key, form_vars, form_count, sizeof(var_t),
(int (*)(const void *, const void *))cgi_compare_variables));
}
static int
cgi_initialize_get(void)
{
char *data;
#ifdef DEBUG
puts("Initializing variables using GET method...");
#endif
data = getenv("QUERY_STRING");
if (data == NULL || strlen(data) == 0)
return (0);
return (cgi_initialize_string(data));
}
static int
cgi_initialize_post(void)
{
char *content_length,
*data;
int length,
nbytes,
tbytes,
status;
#ifdef DEBUG
puts("Initializing variables using POST method...");
#endif
content_length = getenv("CONTENT_LENGTH");
if (content_length == NULL || atoi(content_length) <= 0)
return (0);
length = atoi(content_length);
data = malloc(length + 1);
if (data == NULL)
return (0);
for (tbytes = 0; tbytes < length; tbytes += nbytes)
if ((nbytes = read(0, data + tbytes, length - tbytes)) < 0)
if (errno != EAGAIN)
{
free(data);
return (0);
}
data[length] = '\0';
status = cgi_initialize_string(data);
free(data);
return (status);
}
static int
cgi_initialize_string(const char *data)
{
int done;
char *s,
ch,
name[255],
value[65536];
if (data == NULL)
return (0);
while (*data != '\0')
{
for (s = name; *data != '\0'; data ++)
if (*data == '=')
break;
else if (*data >= ' ' && s < (name + sizeof(name) - 1))
*s++ = *data;
*s = '\0';
if (*data == '=')
data ++;
else
return (0);
for (s = value, done = 0; !done && *data != '\0'; data ++)
switch (*data)
{
case '&' :
done = 1;
break;
case '+' :
if (s < (value + sizeof(value) - 1))
*s++ = ' ';
break;
case '%' :
if (s < (value + sizeof(value) - 1))
{
data ++;
ch = *data - '0';
if (ch > 9)
ch -= 7;
*s = ch << 4;
data ++;
ch = *data - '0';
if (ch > 9)
ch -= 7;
*s++ |= ch;
}
else
data += 2;
break;
default :
if (*data >= ' ' && s < (value + sizeof(value) - 1))
*s++ = *data;
break;
}
*s = '\0';
if (s > value)
s --;
while (s >= value && *s == ' ')
*s-- = '\0';
if ((s = strrchr(name, '-')) != NULL && isdigit(s[1]))
{
*s++ = '\0';
if (value[0])
cgiSetArray(name, atoi(s) - 1, value);
}
else if (cgiGetVariable(name) != NULL)
cgiSetArray(name, cgiGetSize(name), value);
else
cgiSetVariable(name, value);
}
return (1);
}
static void
cgi_sort_variables(void)
{
#ifdef DEBUG
int i;
puts("Sorting variables...");
#endif
if (form_count < 2)
return;
qsort(form_vars, form_count, sizeof(var_t),
(int (*)(const void *, const void *))cgi_compare_variables);
#ifdef DEBUG
puts("Sorted variable list is:");
for (i = 0; i < form_count; i ++)
printf("%d: %s (%d) = \"%s\" ...\n", i, form_vars[i].name,
form_vars[i].nvalues, form_vars[i].values[0]);
#endif
}