diff --git a/include/http.h b/include/http.h index 8c1cf82..d1a2a9f 100644 --- a/include/http.h +++ b/include/http.h @@ -48,6 +48,7 @@ typedef struct http_request { } body; size_t body_len; size_t body_received; + char *body_file_name; regmatch_t regex_groups[HTTP_MAX_REGEX_GROUPS]; struct phr_header headers[HTTP_MAX_ALLOWED_HEADERS]; size_t num_headers; diff --git a/include/http_loop.h b/include/http_loop.h index edae126..5e10932 100644 --- a/include/http_loop.h +++ b/include/http_loop.h @@ -111,11 +111,15 @@ void http_loop_res_add_header(http_loop_ctx *ctx, http_header type, */ bool http_loop_step_body_to_buf(event_loop_conn *conn); +bool http_loop_step_body_to_file(event_loop_conn *conn); + /* * Authenticate the request using the X-Api-Key header */ bool http_loop_step_auth(event_loop_conn *conn); +bool http_loop_step_switch_res(event_loop_conn *conn); + /** * Initialize a new http loop */ diff --git a/include/lander.h b/include/lander.h index cb3dd69..daf6fe7 100644 --- a/include/lander.h +++ b/include/lander.h @@ -3,7 +3,7 @@ #include "http_loop.h" -extern http_route lander_routes[3]; +extern http_route lander_routes[4]; bool lander_get_index(event_loop_conn *conn); @@ -11,4 +11,6 @@ bool lander_get_entry(event_loop_conn *conn); bool lander_post_redirect(event_loop_conn *conn); +bool lander_post_paste(event_loop_conn *conn); + #endif diff --git a/src/http_loop/http_loop_tools.c b/src/http_loop/http_loop_tools.c index 68e266e..aa5a71e 100644 --- a/src/http_loop/http_loop_tools.c +++ b/src/http_loop/http_loop_tools.c @@ -53,35 +53,50 @@ static bool string_to_num(size_t *res, const char *s, size_t len) { return true; } +/* + * Try to find and parse the Content-Length header. This function returns true + * if it was successful. If false is returned, the underlying step should + * immediately exit. + */ +static bool try_parse_content_length(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; + + for (size_t i = 0; i < ctx->req.num_headers; i++) { + struct phr_header *header = &ctx->req.headers[i]; + + if (strncmp(header->name, "Content-Length", header->name_len) == 0) { + // If the content length header is present but contains an invalid + // number, we return a bad request error + if (!string_to_num(&ctx->req.body_len, header->value, + header->value_len)) { + ctx->res.status = http_bad_request; + conn->state = event_loop_conn_state_res; + + return false; + } + // The content length was actually 0, so we can instantly return here + else if (ctx->req.body_len == 0) { + return false; + } + } + } + + // A zero here means there's no content length header + if (ctx->req.body_len == 0) { + ctx->res.status = http_length_required; + conn->state = event_loop_conn_state_res; + + return false; + } + + return true; +} + bool http_loop_step_body_to_buf(event_loop_conn *conn) { http_loop_ctx *ctx = conn->ctx; if (ctx->req.body_len == 0) { - for (size_t i = 0; i < ctx->req.num_headers; i++) { - struct phr_header *header = &ctx->req.headers[i]; - - if (strncmp(header->name, "Content-Length", header->name_len) == 0) { - // If the content length header is present but contains an invalid - // number, we return a bad request error - if (!string_to_num(&ctx->req.body_len, header->value, - header->value_len)) { - ctx->res.status = http_bad_request; - conn->state = event_loop_conn_state_res; - - return true; - } - // The content length was actually 0, so we can instantly return here - else if (ctx->req.body_len == 0) { - return true; - } - } - } - - // A zero here means there's no content length header - if (ctx->req.body_len == 0) { - ctx->res.status = http_length_required; - conn->state = event_loop_conn_state_res; - + if (!try_parse_content_length(conn)) { return true; } @@ -95,6 +110,30 @@ bool http_loop_step_body_to_buf(event_loop_conn *conn) { memcpy(&ctx->req.body.buf[ctx->req.body_received], &conn->rbuf[conn->rbuf_read], bytes_to_copy); ctx->req.body_received += bytes_to_copy; + conn->rbuf_read += bytes_to_copy; + + return ctx->req.body_received == ctx->req.body_len; +} + +bool http_loop_step_body_to_file(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; + + if (ctx->req.body_len == 0) { + if (!try_parse_content_length(conn)) { + return true; + } + + ctx->req.body_type = http_body_file; + ctx->req.body.file = fopen(ctx->req.body_file_name, "wb"); + ctx->req.body_received = 0; + } + + size_t bytes_to_write = MIN(conn->rbuf_size - conn->rbuf_read, + ctx->req.body_len - ctx->req.body_received); + size_t bytes_written = fwrite(&conn->rbuf[conn->rbuf_read], sizeof(uint8_t), + bytes_to_write, ctx->req.body.file); + ctx->req.body_received += bytes_written; + conn->rbuf_read += bytes_written; return ctx->req.body_received == ctx->req.body_len; } @@ -116,3 +155,9 @@ bool http_loop_step_auth(event_loop_conn *conn) { return true; } + +bool http_loop_step_switch_res(event_loop_conn *conn) { + conn->state = event_loop_conn_state_res; + + return true; +} diff --git a/src/lander/lander.c b/src/lander/lander.c index fc245f3..7a66082 100644 --- a/src/lander/lander.c +++ b/src/lander/lander.c @@ -15,4 +15,10 @@ http_route lander_routes[] = { .method = http_post, .path = "/s/", .steps = {http_loop_step_auth, http_loop_step_body_to_buf, - lander_post_redirect, NULL}}}; + lander_post_redirect, NULL}}, + {.type = http_route_literal, + .method = http_post, + .path = "/p/", + .steps = {http_loop_step_auth, lander_post_paste, + http_loop_step_body_to_file, http_loop_step_switch_res, NULL}}, +}; diff --git a/src/lander/lander_post.c b/src/lander/lander_post.c index 47f64ea..7bde258 100644 --- a/src/lander/lander_post.c +++ b/src/lander/lander_post.c @@ -38,3 +38,40 @@ bool lander_post_redirect(event_loop_conn *conn) { return true; } + +bool lander_post_paste(event_loop_conn *conn) { + http_loop_ctx *ctx = conn->ctx; + + char *key; + Entry *new_entry = entry_new(Paste, ""); + TrieExitCode res = trie_add_random(ctx->g->trie, &key, new_entry, false); + + if (res != Ok) { + error("trie_add_random failed with exit code %i", res); + + ctx->res.status = http_internal_server_error; + conn->state = event_loop_conn_state_res; + + return true; + } + + // Add a slash to the key and add it as the location header + size_t key_len = strlen(key); + char *buf = malloc(key_len + 2); + + memcpy(&buf[1], key, key_len); + buf[0] = '/'; + buf[key_len + 1] = '\0'; + + http_loop_res_add_header(ctx, http_header_location, buf, true); + + // TODO free this + char *fname = malloc(8 + key_len); + sprintf(fname, "pastes/%s", key); + + ctx->req.body_file_name = fname; + + free(key); + + return true; +}