cbsi/mimetype.c

347 wiersze
7.4 KiB
C

/**
* @PROJECT CGI Bash Shell Interface
* @COPYRIGHT See COPYING in the top level directory
* @FILE mimetype.c
* @PURPOSE Mimetype and file upload handler
* @DEVELOPERS Nathan Angelacos <nangel@users.sourceforge.net>
* Rafal Kupiec <belliash@asiotec.eu.org>
*/
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "buffer.h"
#include "error.h"
#include "subshell.h"
#include "buffer.h"
#include "mimetype.h"
#include "cbsi.h"
void empty_stdin(void) {
char c[2000];
while(read(STDIN_FILENO, &c, 2000)) {
}
}
void mime_var_init(mime_var_t* obj) {
obj->name = NULL;
obj->filename = NULL;
obj->type = NULL;
obj->tempname = NULL;
buffer_init(&(obj->value));
obj->fh = 0;
}
void mime_var_destroy(mime_var_t* obj) {
int status;
struct sigaction new_action;
if(obj->name) {
free(obj->name);
obj->name = NULL;
}
if(obj->filename) {
free(obj->filename);
obj->filename = NULL;
}
if(obj->type) {
free(obj->type);
obj->type = NULL;
}
if(obj->tempname) {
free(obj->tempname);
obj->tempname = NULL;
}
buffer_destroy(&(obj->value));
if(obj->fh) {
close(abs(obj->fh));
obj->fh = 0;
}
}
char* mime_substr(char* start, int len) {
char* ptr;
if(!start) {
return NULL;
}
if(len < 0) {
return NULL;
}
ptr = xmalloc(len + 2);
memcpy(ptr, start, len);
return ptr;
}
void mime_tag_add(mime_var_t* obj, char* str) {
char* a = NULL;
char* b = NULL;
static char* tag[] = { "name=\"", "filename=\"", "Content-Type: " };
a = strcasestr(str, tag[0]);
if(a) {
a += strlen(tag[0]);
b = strchr(a, '"');
if(!obj->name) {
obj->name = mime_substr(a, b - a);
}
}
a = strcasestr(str, tag[1]);
if(a) {
a += strlen(tag[1]);
b = strchr(a, '"');
if(!obj->filename) {
obj->filename = mime_substr(a, b - a);
}
}
a = strcasestr(str, tag[2]);
if(a) {
a += strlen(tag[2]);
b = a + strlen(a);
if(!obj->type) {
obj->type = mime_substr(a, b - a);
}
}
}
void mime_var_putenv(list_t* env, mime_var_t* obj) {
buffer_t buf;
buffer_init(&buf);
if(obj->filename) {
buffer_add(&buf, obj->name, strlen(obj->name));
buffer_add(&buf, "=", 1);
buffer_add(&buf, (char*) obj->value.data,
strlen((char*) obj->value.data) + 1);
myputenv(env, (char*) buf.data, global.file_prefix);
buffer_reset(&buf);
buffer_add(&buf, obj->name, strlen(obj->name));
buffer_add(&buf, "=", 1);
buffer_add(&buf, obj->filename, strlen(obj->filename) + 1);
myputenv(env, (char*) buf.data, global.filename_prefix);
buffer_reset (&buf);
} else if(obj->name) {
buffer_add(&(obj->value), "", 1);
buffer_add(&buf, obj->name, strlen(obj->name));
buffer_add(&buf, "=", 1);
buffer_add(&buf, (char*) obj->value.data,
strlen((char*) obj->value.data) + 1);
myputenv(env, (char*) buf.data, global.file_prefix);
buffer_reset(&buf);
}
buffer_destroy(&buf);
}
void mime_exec(mime_var_t* obj, char* fifo) {
int pid;
char* type;
char* filename;
char* name;
char *c;
int fh;
struct sigaction new_action;
pid = fork();
if(pid == -1) {
empty_stdin();
die_with_message(NULL, NULL, g_err_msg[E_SUBSHELL_FAIL]);
}
if(pid == 0) {
if(obj->type) {
type = xmalloc(13 + strlen(obj->type) + 1);
sprintf(type, "CONTENT_TYPE=%s", obj->type);
putenv(type);
}
if(obj->filename) {
filename = xmalloc (9 + strlen(obj->filename) + 1);
sprintf(filename, "FILENAME=%s", obj->filename);
putenv(filename);
}
if(obj->name) {
name = xmalloc(5 + strlen(obj->name) + 1);
sprintf(name, "NAME=%s", obj->name);
putenv(name);
}
fh = open(fifo, O_RDONLY);
while(read(fh, &c, 1)) {
}
exit(-1);
} else {
new_action.sa_handler = SIG_IGN;
sigemptyset(&new_action.sa_mask);
new_action.sa_flags = 0;
sigaction(SIGPIPE, &new_action, NULL);
}
}
void mime_var_open_target(mime_var_t* obj) {
char* tmpname;
token_t* curtoken;
curtoken = global.uploadlist;
int ok;
if(global.uploadkb == 0) {
empty_stdin();
die_with_message(NULL, NULL, "File uploads are not allowed.");
}
ok = -1;
tmpname = xmalloc(strlen(global.uploaddir) + 8);
strcpy(tmpname, global.uploaddir);
strcat(tmpname, "/XXXXXX");
obj->fh = mkstemp(tmpname);
if(obj->fh == -1) {
ok = 0;
}
buffer_add(&(obj->value), tmpname, strlen(tmpname));
if(!ok) {
empty_stdin();
die_with_message(NULL, NULL, g_err_msg[E_FILE_OPEN_FAIL], tmpname);
}
curtoken = push_token_on_list(curtoken, NULL, tmpname, strlen(tmpname) + 1);
if(global.uploadlist == NULL) {
global.uploadlist = curtoken;
}
}
void mime_var_writer(mime_var_t* obj, char* str, int len) {
int err;
if(!obj->filename) {
buffer_add(&(obj->value), str, len);
}
if((!obj->fh) && (obj->filename)) {
mime_var_open_target(obj);
}
if(obj->fh > 0) {
err = write(obj->fh, str, len);
if(err == -1) {
obj->fh = abs(obj->fh) * -1;
}
}
}
int rfc2388_handler(list_t* env) {
enum mime_state_t { DISCARD, BOUNDARY, HEADER, CONTENT };
int state;
int i, x;
unsigned long max_len, content_length;
sbuffer_t sbuf;
char* crlf = "\r\n";
char* boundary;
char* str;
buffer_t buf;
mime_var_t var;
str = getenv("CONTENT_TYPE");
i = strlen(str) - 9;
while((i >= 0) && (memcmp("boundary=", str + i, 9))) {
i--;
}
if(i == -1) {
empty_stdin();
die_with_message(NULL, NULL, "No Mime Boundary Information Found");
}
i = i + 9;
if(str[i] == '"') {
i++;
}
boundary = xmalloc(strlen(str + i) + 5);
memcpy(boundary, crlf, 2);
memcpy(boundary + 2, "--", 2);
memcpy(boundary + 4, str + i, strlen(str + i) + 1);
if((i > 0) && (str[i - 1] == '"')) {
while((boundary[i]) && (boundary[i] != '"')) {
i++;
}
boundary[i] = '\0';
}
max_len = ((global.uploadkb == 0) ? 2048 : abs(global.uploadkb)) * 1024;
content_length = 0;
sbuffer_init(&sbuf, 1024 * 128);
sbuf.fh = STDIN;
if(getenv("CONTENT_LENGTH")) {
sbuf.maxread = strtoul(getenv("CONTENT_LENGTH"), NULL, 10);
}
buffer_init(&buf);
buffer_add(&buf, "", 1);
buffer_reset(&buf);
state = DISCARD;
str = boundary + 2;
do {
x = sbuffer_read(&sbuf, str);
content_length += sbuf.len;
if(content_length >= max_len && global.uploadkb != -1) {
empty_stdin();
free(boundary);
sbuffer_destroy(&sbuf);
buffer_destroy(&buf);
if(var.name) {
mime_var_destroy(&var);
}
die_with_message(NULL, NULL, "Attempted to send content larger than allowed limits.");
}
switch(state) {
case DISCARD:
if(x) {
state = BOUNDARY;
str = crlf;
buffer_reset(&buf);
}
break;
case BOUNDARY:
if(x) {
buffer_add(&buf, sbuf.segment, sbuf.len);
if(!memcmp(buf.data, boundary + 2, 2)) {
str = boundary + 2;
state = DISCARD;
} else {
buffer_reset(&buf);
mime_var_init(&var);
state = HEADER;
str = crlf;
}
} else {
buffer_add(&buf, sbuf.segment, sbuf.len);
}
break;
case HEADER:
buffer_add(&buf, sbuf.segment, sbuf.len);
if(x) {
if(sbuf.len == 0) {
buffer_reset(&buf);
state = CONTENT;
str = boundary;
} else {
buffer_add(&buf, "", 1);
mime_tag_add(&var, (char*) buf.data);
buffer_reset(&buf);
}
}
break;
case CONTENT:
mime_var_writer(&var, (char*) sbuf.segment, sbuf.len);
if(x) {
buffer_reset(&buf);
mime_var_putenv(env, &var);
mime_var_destroy(&var);
state = BOUNDARY;
str = crlf;
}
break;
}
} while(!sbuf.eof);
free(boundary);
sbuffer_destroy(&sbuf);
buffer_destroy(&buf);
return 0;
}