#include "cgi-private.h"
#include <errno.h>
typedef struct
{
const char *name;
int nvalues,
avalues;
const char **values;
} _cgi_var_t;
static int form_count = 0,
form_alloc = 0;
static _cgi_var_t *form_vars = NULL;
static cgi_file_t *form_file = NULL;
static void cgi_add_variable(const char *name, int element,
const char *value);
static int cgi_compare_variables(const _cgi_var_t *v1,
const _cgi_var_t *v2);
static _cgi_var_t *cgi_find_variable(const char *name);
static int cgi_initialize_get(void);
static int cgi_initialize_multipart(const char *boundary);
static int cgi_initialize_post(void);
static int cgi_initialize_string(const char *data);
static const char *cgi_passwd(const char *prompt);
static void cgi_sort_variables(void);
static void cgi_unlink_file(void);
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)
{
_cgi_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]);
}
const cgi_file_t *
cgiGetFile(void)
{
return (form_file);
}
int
cgiGetSize(const char *name)
{
_cgi_var_t *var;
if ((var = cgi_find_variable(name)) == NULL)
return (0);
return (var->nvalues);
}
const char *
cgiGetVariable(const char *name)
{
const _cgi_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]);
}
int
cgiInitialize(void)
{
const char *method;
const char *content_type;
cupsSetPasswordCB(cgi_passwd);
setlocale(LC_ALL, "");
#ifdef DEBUG
setbuf(stdout, NULL);
puts("Content-type: text/plain\n");
#endif
method = getenv("REQUEST_METHOD");
content_type = getenv("CONTENT_TYPE");
if (!method)
return (0);
if (!strcasecmp(method, "GET"))
return (cgi_initialize_get());
else if (!strcasecmp(method, "POST") && content_type)
{
const char *boundary = strstr(content_type, "boundary=");
if (boundary)
boundary += 9;
if (content_type && !strncmp(content_type, "multipart/form-data; ", 21))
return (cgi_initialize_multipart(boundary));
else
return (cgi_initialize_post());
}
else
return (0);
}
int
cgiIsPOST(void)
{
const char *method;
if ((method = getenv("REQUEST_METHOD")) == NULL)
return (0);
else
return (!strcmp(method, "POST"));
}
void
cgiSetArray(const char *name,
int element,
const char *value)
{
int i;
_cgi_var_t *var;
if (name == NULL || value == NULL || element < 0 || element > 100000)
return;
if ((var = cgi_find_variable(name)) == NULL)
{
cgi_add_variable(name, element, value);
cgi_sort_variables();
}
else
{
if (element >= var->avalues)
{
const char **temp;
temp = (const char **)realloc((void *)(var->values),
sizeof(char *) * (element + 16));
if (!temp)
return;
var->avalues = element + 16;
var->values = temp;
}
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;
_cgi_var_t *var;
if (name == NULL || size < 0 || size > 100000)
return;
if ((var = cgi_find_variable(name)) == NULL)
return;
if (size >= var->avalues)
{
const char **temp;
temp = (const char **)realloc((void *)(var->values),
sizeof(char *) * (size + 16));
if (!temp)
return;
var->avalues = size + 16;
var->values = temp;
}
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;
_cgi_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)
{
_cgi_var_t *var;
if (name == NULL || value == NULL || element < 0 || element > 100000)
return;
#ifdef DEBUG
printf("Adding variable \'%s\' with value \'%s\'...\n", name, value);
#endif
if (form_count >= form_alloc)
{
_cgi_var_t *temp_vars;
if (form_alloc == 0)
temp_vars = malloc(sizeof(_cgi_var_t) * 16);
else
temp_vars = realloc(form_vars, (form_alloc + 16) * sizeof(_cgi_var_t));
if (!temp_vars)
return;
form_vars = temp_vars;
form_alloc += 16;
}
var = form_vars + form_count;
if ((var->values = calloc(element + 1, sizeof(char *))) == NULL)
return;
var->name = strdup(name);
var->nvalues = element + 1;
var->avalues = element + 1;
var->values[element] = strdup(value);
form_count ++;
}
static int
cgi_compare_variables(
const _cgi_var_t *v1,
const _cgi_var_t *v2)
{
return (strcasecmp(v1->name, v2->name));
}
static _cgi_var_t *
cgi_find_variable(const char *name)
{
_cgi_var_t key;
if (form_count < 1 || name == NULL)
return (NULL);
key.name = name;
return ((_cgi_var_t *)bsearch(&key, form_vars, form_count, sizeof(_cgi_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_multipart(
const char *boundary)
{
char line[10240],
name[1024],
filename[1024],
mimetype[1024],
bstring[256],
*ptr,
*end;
int ch,
fd,
blen;
DEBUG_printf(("cgi_initialize_multipart(boundary=\"%s\")\n", boundary));
name[0] = '\0';
filename[0] = '\0';
mimetype[0] = '\0';
snprintf(bstring, sizeof(bstring), "\r\n--%s", boundary);
blen = strlen(bstring);
while (fgets(line, sizeof(line), stdin))
{
if (!strcmp(line, "\r\n"))
{
if (filename[0])
{
if (form_file)
{
cgi_unlink_file();
}
if ((form_file = calloc(1, sizeof(cgi_file_t))) == NULL)
return (0);
form_file->name = strdup(name);
form_file->filename = strdup(filename);
form_file->mimetype = strdup(mimetype);
fd = cupsTempFd(form_file->tempfile, sizeof(form_file->tempfile));
if (fd < 0)
return (0);
atexit(cgi_unlink_file);
ptr = line;
while ((ch = getchar()) != EOF)
{
*ptr++ = ch;
if ((ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
{
ptr -= blen;
break;
}
if ((ptr - line - blen) >= 8192)
{
write(fd, line, 8192);
memmove(line, line + 8192, ptr - line - 8192);
ptr -= 8192;
}
}
if (ptr > line)
write(fd, line, ptr - line);
close(fd);
}
else
{
ptr = line;
end = line + sizeof(line) - 1;
while ((ch = getchar()) != EOF)
{
if (ptr < end)
*ptr++ = ch;
if ((ptr - line) >= blen && !memcmp(ptr - blen, bstring, blen))
{
ptr -= blen;
break;
}
}
*ptr = '\0';
if ((ptr = strrchr(name, '-')) != NULL && isdigit(ptr[1] & 255))
{
*ptr++ = '\0';
if (line[0])
cgiSetArray(name, atoi(ptr) - 1, line);
}
else if (cgiGetVariable(name))
{
cgiSetArray(name, cgiGetSize(name), line);
}
else
{
cgiSetVariable(name, line);
}
}
fgets(line, sizeof(line), stdin);
name[0] = '\0';
filename[0] = '\0';
mimetype[0] = '\0';
}
else if (!strncasecmp(line, "Content-Disposition:", 20))
{
if ((ptr = strstr(line + 20, " name=\"")) != NULL)
{
strlcpy(name, ptr + 7, sizeof(name));
if ((ptr = strchr(name, '\"')) != NULL)
*ptr = '\0';
}
if ((ptr = strstr(line + 20, " filename=\"")) != NULL)
{
strlcpy(filename, ptr + 11, sizeof(filename));
if ((ptr = strchr(filename, '\"')) != NULL)
*ptr = '\0';
}
}
else if (!strncasecmp(line, "Content-Type:", 13))
{
for (ptr = line + 13; isspace(*ptr & 255); ptr ++);
strlcpy(mimetype, ptr, sizeof(mimetype));
for (ptr = mimetype + strlen(mimetype) - 1;
ptr > mimetype && isspace(*ptr & 255);
*ptr-- = '\0');
}
}
return (1);
}
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);
}
else
nbytes = 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] & 255))
{
*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 const char *
cgi_passwd(const char *prompt)
{
(void)prompt;
fprintf(stderr, "DEBUG: cgi_passwd(prompt=\"%s\") called!\n",
prompt ? prompt : "(null)");
puts("Status: 401\n");
exit(0);
return (NULL);
}
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(_cgi_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
}
static void
cgi_unlink_file(void)
{
if (form_file)
{
unlink(form_file->tempfile);
free(form_file->name);
free(form_file->filename);
free(form_file->mimetype);
free(form_file);
form_file = NULL;
}
}