diff options
author | Adam Chlipala <adamc@hcoop.net> | 2009-04-25 13:59:11 -0400 |
---|---|---|
committer | Adam Chlipala <adamc@hcoop.net> | 2009-04-25 13:59:11 -0400 |
commit | 3ad3130a7006e0a0fb4029d3acd46a4344c4f70a (patch) | |
tree | 780df82b8252d17fbc6a6f3dc7b66ba2c6e5fb41 /src/c | |
parent | 42a6d61be0c529b52d16e34998e96183219aea65 (diff) |
Initial support for blobs and upload
Diffstat (limited to 'src/c')
-rw-r--r-- | src/c/driver.c | 198 | ||||
-rw-r--r-- | src/c/urweb.c | 151 |
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]; +} |