summaryrefslogtreecommitdiff
path: root/src/c
diff options
context:
space:
mode:
authorGravatar Adam Chlipala <adamc@hcoop.net>2009-04-25 13:59:11 -0400
committerGravatar Adam Chlipala <adamc@hcoop.net>2009-04-25 13:59:11 -0400
commit3ad3130a7006e0a0fb4029d3acd46a4344c4f70a (patch)
tree780df82b8252d17fbc6a6f3dc7b66ba2c6e5fb41 /src/c
parent42a6d61be0c529b52d16e34998e96183219aea65 (diff)
Initial support for blobs and upload
Diffstat (limited to 'src/c')
-rw-r--r--src/c/driver.c198
-rw-r--r--src/c/urweb.c151
2 files changed, 307 insertions, 42 deletions
diff --git a/src/c/driver.c b/src/c/driver.c
index f7456ed9..fa4f474b 100644
--- a/src/c/driver.c
+++ b/src/c/driver.c
@@ -1,5 +1,6 @@
-#include <stdio.h>
+#define _GNU_SOURCE
+#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -147,9 +148,11 @@ void uw_sign(const char *in, char *out) {
static void *worker(void *data) {
int me = *(int *)data, retries_left = MAX_RETRIES;
uw_context ctx = new_context();
+ size_t buf_size = 1;
+ char *buf = malloc(buf_size);
while (1) {
- char buf[uw_bufsize+1], *back = buf, *s, *post;
+ char *back = buf, *s, *post;
int sock, dont_close = 0;
pthread_mutex_lock(&queue_mutex);
@@ -162,7 +165,17 @@ static void *worker(void *data) {
while (1) {
unsigned retries_left = MAX_RETRIES;
- int r = recv(sock, back, uw_bufsize - (back - buf), 0);
+ int r;
+
+ if (back - buf == buf_size) {
+ char *new_buf;
+ buf_size *= 2;
+ new_buf = realloc(buf, buf_size);
+ back = new_buf + (back - buf);
+ buf = new_buf;
+ }
+
+ r = recv(sock, back, buf_size - (back - buf), 0);
if (r < 0) {
fprintf(stderr, "Recv failed\n");
@@ -182,8 +195,12 @@ static void *worker(void *data) {
if (s = strstr(buf, "\r\n\r\n")) {
failure_kind fk;
int is_post = 0;
+ char *boundary = NULL;
+ size_t boundary_len;
char *cmd, *path, *headers, path_copy[uw_bufsize+1], *inputs, *after_headers;
+ //printf("All: %s\n", buf);
+
s[2] = 0;
after_headers = s + 4;
@@ -196,7 +213,7 @@ static void *worker(void *data) {
headers = s + 2;
cmd = s = buf;
- printf("Read: %s\n", buf);
+ //printf("Read: %s\n", buf);
if (!strsep(&s, " ")) {
fprintf(stderr, "No first space in HTTP command\n");
@@ -208,17 +225,25 @@ static void *worker(void *data) {
if (!strcmp(cmd, "POST")) {
char *clen_s = uw_Basis_requestHeader(ctx, "Content-length");
if (!clen_s) {
- printf("No Content-length with POST\n");
+ fprintf(stderr, "No Content-length with POST\n");
goto done;
}
int clen = atoi(clen_s);
if (clen < 0) {
- printf("Negative Content-length with POST\n");
+ fprintf(stderr, "Negative Content-length with POST\n");
goto done;
}
while (back - after_headers < clen) {
- r = recv(sock, back, uw_bufsize - (back - buf), 0);
+ if (back - buf == buf_size) {
+ char *new_buf;
+ buf_size *= 2;
+ new_buf = realloc(buf, buf_size);
+ back = new_buf + (back - buf);
+ buf = new_buf;
+ }
+
+ r = recv(sock, back, buf_size - (back - buf), 0);
if (r < 0) {
fprintf(stderr, "Recv failed\n");
@@ -235,6 +260,19 @@ static void *worker(void *data) {
}
is_post = 1;
+
+ clen_s = uw_Basis_requestHeader(ctx, "Content-type");
+ if (clen_s && !strncasecmp(clen_s, "multipart/form-data", 19)) {
+ if (strncasecmp(clen_s + 19, "; boundary=", 11)) {
+ fprintf(stderr, "Bad multipart boundary spec");
+ break;
+ }
+
+ boundary = clen_s + 28;
+ boundary[0] = '-';
+ boundary[1] = '-';
+ boundary_len = strlen(boundary);
+ }
} else if (strcmp(cmd, "GET")) {
fprintf(stderr, "Not ready for non-GET/POST command: %s\n", cmd);
break;
@@ -262,26 +300,134 @@ static void *worker(void *data) {
break;
}
- if (is_post)
- inputs = after_headers;
- else if (inputs = strchr(path, '?'))
- *inputs++ = 0;
- if (inputs) {
- char *name, *value;
-
- while (*inputs) {
- name = inputs;
- if (inputs = strchr(inputs, '&'))
- *inputs++ = 0;
- else
- inputs = strchr(name, 0);
-
- if (value = strchr(name, '=')) {
- *value++ = 0;
- uw_set_input(ctx, name, value);
+ if (boundary) {
+ char *part = after_headers, *after_sub_headers, *header, *after_header;
+ size_t part_len;
+
+ part = strstr(part, boundary);
+ if (!part) {
+ fprintf(stderr, "Missing first multipart boundary\n");
+ break;
+ }
+ part += boundary_len;
+
+ while (1) {
+ char *name = NULL, *filename = NULL, *type = NULL;
+
+ if (part[0] == '-' && part[1] == '-')
+ break;
+
+ if (*part != '\r') {
+ fprintf(stderr, "No \\r after multipart boundary\n");
+ goto done;
+ }
+ ++part;
+ if (*part != '\n') {
+ fprintf(stderr, "No \\n after multipart boundary\n");
+ goto done;
+ }
+ ++part;
+
+ if (!(after_sub_headers = strstr(part, "\r\n\r\n"))) {
+ fprintf(stderr, "Missing end of headers after multipart boundary\n");
+ goto done;
+ }
+ after_sub_headers[2] = 0;
+ after_sub_headers += 4;
+
+ for (header = part; after_header = strstr(header, "\r\n"); header = after_header + 2) {
+ char *colon, *after_colon;
+
+ *after_header = 0;
+ if (!(colon = strchr(header, ':'))) {
+ fprintf(stderr, "Missing colon in multipart sub-header\n");
+ goto done;
+ }
+ *colon++ = 0;
+ if (*colon++ != ' ') {
+ fprintf(stderr, "No space after colon in multipart sub-header\n");
+ goto done;
+ }
+
+ if (!strcasecmp(header, "Content-Disposition")) {
+ if (strncmp(colon, "form-data; ", 11)) {
+ fprintf(stderr, "Multipart data is not \"form-data\"\n");
+ goto done;
+ }
+
+ for (colon += 11; after_colon = strchr(colon, '='); colon = after_colon) {
+ char *data;
+ after_colon[0] = 0;
+ if (after_colon[1] != '"') {
+ fprintf(stderr, "Disposition setting is missing initial quote\n");
+ goto done;
+ }
+ data = after_colon+2;
+ if (!(after_colon = strchr(data, '"'))) {
+ fprintf(stderr, "Disposition setting is missing final quote\n");
+ goto done;
+ }
+ after_colon[0] = 0;
+ ++after_colon;
+ if (after_colon[0] == ';' && after_colon[1] == ' ')
+ after_colon += 2;
+
+ if (!strcasecmp(colon, "name"))
+ name = data;
+ else if (!strcasecmp(colon, "filename"))
+ filename = data;
+ }
+ } else if (!strcasecmp(header, "Content-Type")) {
+ type = colon;
+ }
+ }
+
+ part = memmem(after_sub_headers, back - after_sub_headers, boundary, boundary_len);
+ if (!part) {
+ fprintf(stderr, "Missing boundary after multipart payload\n");
+ goto done;
+ }
+ part[-2] = 0;
+ part_len = part - after_sub_headers - 2;
+ part[0] = 0;
+ part += boundary_len;
+
+ if (filename) {
+ uw_Basis_file *f = malloc(sizeof(uw_Basis_file));
+ uw_Basis_files fs = { 1, f };
+
+ f->name = filename;
+ f->data.size = part_len;
+ f->data.data = after_sub_headers;
+
+ uw_set_file_input(ctx, name, fs);
+ } else
+ uw_set_input(ctx, name, after_sub_headers);
+ }
+ }
+ else {
+ if (is_post)
+ inputs = after_headers;
+ else if (inputs = strchr(path, '?'))
+ *inputs++ = 0;
+
+ if (inputs) {
+ char *name, *value;
+
+ while (*inputs) {
+ name = inputs;
+ if (inputs = strchr(inputs, '&'))
+ *inputs++ = 0;
+ else
+ inputs = strchr(name, 0);
+
+ if (value = strchr(name, '=')) {
+ *value++ = 0;
+ uw_set_input(ctx, name, value);
+ }
+ else
+ uw_set_input(ctx, name, "");
}
- else
- uw_set_input(ctx, name, "");
}
}
diff --git a/src/c/urweb.c b/src/c/urweb.c
index 6266e12d..99564605 100644
--- a/src/c/urweb.c
+++ b/src/c/urweb.c
@@ -282,11 +282,23 @@ typedef struct {
buf msgs;
} delta;
+typedef enum {
+ UNSET, NORMAL, FILES
+} input_kind;
+
+typedef struct {
+ input_kind kind;
+ union {
+ char *normal;
+ uw_Basis_files files;
+ } data;
+} input;
+
struct uw_context {
char *headers, *headers_end;
buf outHeaders, page, heap, script;
- char **inputs;
+ input *inputs;
int source_count;
@@ -325,7 +337,7 @@ uw_context uw_init() {
buf_init(&ctx->script, 1);
ctx->script.start[0] = 0;
- ctx->inputs = calloc(uw_inputs_len, sizeof(char *));
+ ctx->inputs = calloc(uw_inputs_len, sizeof(input));
ctx->db = NULL;
@@ -398,7 +410,7 @@ void uw_reset_keep_request(uw_context ctx) {
void uw_reset(uw_context ctx) {
uw_reset_keep_request(ctx);
- memset(ctx->inputs, 0, uw_inputs_len * sizeof(char *));
+ memset(ctx->inputs, 0, uw_inputs_len * sizeof(input));
}
void uw_db_init(uw_context);
@@ -544,9 +556,9 @@ char *uw_error_message(uw_context ctx) {
return ctx->error_message;
}
-int uw_input_num(char*);
+extern int uw_input_num(const char*);
-void uw_set_input(uw_context ctx, char *name, char *value) {
+void uw_set_input(uw_context ctx, const char *name, char *value) {
int n = uw_input_num(name);
if (n < 0)
@@ -555,9 +567,8 @@ void uw_set_input(uw_context ctx, char *name, char *value) {
if (n >= uw_inputs_len)
uw_error(ctx, FATAL, "For input name %s, index %d is out of range", name, n);
- ctx->inputs[n] = value;
-
- //printf("[%d] %s = %s\n", n, name, value);
+ ctx->inputs[n].kind = NORMAL;
+ ctx->inputs[n].data.normal = value;
}
char *uw_get_input(uw_context ctx, int n) {
@@ -565,8 +576,17 @@ char *uw_get_input(uw_context ctx, int n) {
uw_error(ctx, FATAL, "Negative input index %d", n);
if (n >= uw_inputs_len)
uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
- //printf("[%d] = %s\n", n, ctx->inputs[n]);
- return ctx->inputs[n];
+
+ switch (ctx->inputs[n].kind) {
+ case UNSET:
+ return NULL;
+ case FILES:
+ uw_error(ctx, FATAL, "Tried to read a files form input as normal");
+ case NORMAL:
+ return ctx->inputs[n].data.normal;
+ default:
+ uw_error(ctx, FATAL, "Impossible input kind");
+ }
}
char *uw_get_optional_input(uw_context ctx, int n) {
@@ -574,8 +594,51 @@ char *uw_get_optional_input(uw_context ctx, int n) {
uw_error(ctx, FATAL, "Negative input index %d", n);
if (n >= uw_inputs_len)
uw_error(ctx, FATAL, "Out-of-bounds input index %d", n);
- //printf("[%d] = %s\n", n, ctx->inputs[n]);
- return (ctx->inputs[n] == NULL ? "" : ctx->inputs[n]);
+
+ switch (ctx->inputs[n].kind) {
+ case UNSET:
+ return "";
+ case FILES:
+ uw_error(ctx, FATAL, "Tried to read a files form input as normal");
+ case NORMAL:
+ return ctx->inputs[n].data.normal;
+ default:
+ uw_error(ctx, FATAL, "Impossible input kind");
+ }
+}
+
+void uw_set_file_input(uw_context ctx, const char *name, uw_Basis_files fs) {
+ int n = uw_input_num(name);
+
+ if (n < 0)
+ uw_error(ctx, FATAL, "Bad file input name %s", name);
+
+ if (n >= uw_inputs_len)
+ uw_error(ctx, FATAL, "For file input name %s, index %d is out of range", name, n);
+
+ ctx->inputs[n].kind = FILES;
+ ctx->inputs[n].data.files = fs;
+}
+
+uw_Basis_files uw_get_file_input(uw_context ctx, int n) {
+ if (n < 0)
+ uw_error(ctx, FATAL, "Negative file input index %d", n);
+ if (n >= uw_inputs_len)
+ uw_error(ctx, FATAL, "Out-of-bounds file input index %d", n);
+
+ switch (ctx->inputs[n].kind) {
+ case UNSET:
+ {
+ uw_Basis_files fs = {};
+ return fs;
+ }
+ case FILES:
+ return ctx->inputs[n].data.files;
+ case NORMAL:
+ uw_error(ctx, FATAL, "Tried to read a normal form input as files");
+ default:
+ uw_error(ctx, FATAL, "Impossible input kind");
+ }
}
void uw_set_script_header(uw_context ctx, const char *s) {
@@ -1393,7 +1456,7 @@ uw_Basis_string uw_Basis_strcat(uw_context ctx, uw_Basis_string s1, uw_Basis_str
return s;
}
-uw_Basis_string uw_Basis_strdup(uw_context ctx, uw_Basis_string s1) {
+uw_Basis_string uw_strdup(uw_context ctx, uw_Basis_string s1) {
int len = strlen(s1) + 1;
char *s;
@@ -1407,9 +1470,9 @@ uw_Basis_string uw_Basis_strdup(uw_context ctx, uw_Basis_string s1) {
return s;
}
-uw_Basis_string uw_Basis_maybe_strdup(uw_context ctx, uw_Basis_string s1) {
+uw_Basis_string uw_maybe_strdup(uw_context ctx, uw_Basis_string s1) {
if (s1)
- return uw_Basis_strdup(ctx, s1);
+ return uw_strdup(ctx, s1);
else
return NULL;
}
@@ -1477,7 +1540,7 @@ uw_Basis_string uw_Basis_sqlifyString(uw_context ctx, uw_Basis_string s) {
if (isprint(c))
*s2++ = c;
else {
- sprintf(s2, "\\%3o", c);
+ sprintf(s2, "\\%03o", c);
s2 += 4;
}
}
@@ -1488,6 +1551,43 @@ uw_Basis_string uw_Basis_sqlifyString(uw_context ctx, uw_Basis_string s) {
return r;
}
+uw_Basis_string uw_Basis_sqlifyBlob(uw_context ctx, uw_Basis_blob b) {
+ char *r, *s2;
+ size_t i;
+
+ uw_check_heap(ctx, b.size * 5 + 11);
+
+ r = s2 = ctx->heap.front;
+ *s2++ = 'E';
+ *s2++ = '\'';
+
+ for (i = 0; i < b.size; ++i) {
+ char c = b.data[i];
+
+ switch (c) {
+ case '\'':
+ strcpy(s2, "\\'");
+ s2 += 2;
+ break;
+ case '\\':
+ strcpy(s2, "\\\\\\\\");
+ s2 += 4;
+ break;
+ default:
+ if (isprint(c))
+ *s2++ = c;
+ else {
+ sprintf(s2, "\\\\%03o", c);
+ s2 += 5;
+ }
+ }
+ }
+
+ strcpy(s2, "'::bytea");
+ ctx->heap.front = s2 + 9;
+ return r;
+}
+
char *uw_Basis_sqlifyChannel(uw_context ctx, uw_Basis_channel chn) {
int len;
char *r;
@@ -2020,3 +2120,22 @@ uw_Basis_string uw_Basis_makeSigString(uw_context ctx, uw_Basis_string sig) {
uw_Basis_string uw_Basis_sigString(uw_context ctx, uw_unit u) {
return uw_cookie_sig(ctx);
}
+
+uw_Basis_string uw_Basis_fileName(uw_context ctx, uw_Basis_file f) {
+ return f.name;
+}
+
+uw_Basis_blob uw_Basis_fileData(uw_context ctx, uw_Basis_file f) {
+ return f.data;
+}
+
+uw_Basis_int uw_Basis_numFiles(uw_context ctx, uw_Basis_files fs) {
+ return fs.size;
+}
+
+uw_Basis_file uw_Basis_fileNum(uw_context ctx, uw_Basis_files fs, uw_Basis_int n) {
+ if (n < 0 || n >= fs.size)
+ uw_error(ctx, FATAL, "Files index out of bounds");
+ else
+ return fs.files[n];
+}