lander/src/lander/lander_post.c

259 lines
6.3 KiB
C

#include "http/res.h"
#include "http/types.h"
#include "lander.h"
#include "log.h"
#include "lsm/store.h"
static void randomize_key(char *key, int len) {
for (int i = 0; i < len; i++) {
key[i] = charset[rand() % charset_len];
}
key[len] = '\0';
}
// TODO entry leaks if key is already present
static bool add_entry(char **key_ptr, int *key_len_ptr, http_loop_ctx *ctx,
Entry *entry, bool random) {
lander_gctx *c_gctx = ctx->g->c;
// The first match group matches the "long" path
bool secure =
(ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so) == 1;
char *key;
int key_len = 0;
TrieExitCode res;
if (random) {
res = trie_add_random(c_gctx->trie, &key, entry, secure);
if (res == Ok) {
key_len = strlen(key);
}
} else {
key = (char *)&ctx->req.path[ctx->req.regex_groups[2].rm_so];
key_len = ctx->req.regex_groups[2].rm_eo - ctx->req.regex_groups[2].rm_so;
res = trie_add_len(c_gctx->trie, key, key_len, entry);
}
switch (res) {
case Ok:
break;
case AlreadyPresent:
ctx->res.status = http_conflict;
return false;
default:
ctx->res.status = http_internal_server_error;
return false;
}
// Add a slash to the key and add it as the location header
char *buf = malloc(key_len + 2);
memcpy(&buf[1], key, key_len);
buf[0] = '/';
buf[key_len + 1] = '\0';
http_res_add_header(&ctx->res, http_header_location, buf, true);
ctx->res.status = http_created;
if (key_ptr != NULL) {
*key_ptr = key;
}
if (key_len_ptr != NULL) {
*key_len_ptr = key_len;
}
return true;
}
/**
* Insert a new entry into the store.
*
* @return true on success, false otherwise
*/
bool lander_insert_entry(http_loop_ctx *ctx) {
http_loop_gctx *gctx = ctx->g;
lander_gctx *c_gctx = gctx->c;
lander_ctx *c_ctx = ctx->c;
lsm_str *key;
int key_len;
if (ctx->req.regex_groups[2].rm_eo == ctx->req.regex_groups[2].rm_so) {
// Generate a random key to insert
bool secure =
(ctx->req.regex_groups[1].rm_eo - ctx->req.regex_groups[1].rm_so) == 1;
key_len = secure ? 16 : 4;
char *key_s = malloc((key_len + 1) * sizeof(char));
randomize_key(key_s, key_len);
lsm_str_init(&key, key_s);
} else {
char *key_s = (char *)&ctx->req.path[ctx->req.regex_groups[2].rm_so];
key_len = ctx->req.regex_groups[2].rm_eo - ctx->req.regex_groups[2].rm_so;
lsm_str_init_copy_n(&key, key_s, key_len);
}
// TODO free key on error
switch (lsm_store_insert(&c_ctx->entry, c_gctx->store, key)) {
case lsm_error_already_present:
ctx->res.status = http_conflict;
return false;
case lsm_error_ok:
break;
default:
ctx->res.status = http_internal_server_error;
return false;
}
// Add location header
char *buf = malloc(key_len + 2);
memcpy(&buf[1], lsm_str_ptr(key), key_len);
buf[0] = '/';
buf[key_len + 1] = '\0';
http_res_add_header(&ctx->res, http_header_location, buf, true);
ctx->res.status = http_created;
return true;
}
bool lander_post_redirect_lsm(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
lander_ctx *c_ctx = ctx->c;
if (!lander_insert_entry(ctx)) {
conn->state = event_loop_conn_state_res;
return true;
}
lsm_entry_attr_insert_num(c_ctx->entry, lsm_attr_type_entry_type,
lander_entry_type_redirect);
return true;
}
bool lander_post_redirect_body_to_attr(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
lander_ctx *c_ctx = ctx->c;
lsm_str *attr_value;
lsm_str_init_copy_n(&attr_value, ctx->req.body.buf, ctx->req.body.len);
lsm_entry_attr_insert(c_ctx->entry, lsm_attr_type_url, attr_value);
return true;
}
bool lander_entry_sync(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
http_loop_gctx *gctx = ctx->g;
lander_gctx *c_gctx = gctx->c;
lander_ctx *c_ctx = ctx->c;
if (lsm_entry_sync(c_gctx->store, c_ctx->entry) != lsm_error_ok) {
ctx->res.status = http_internal_server_error;
}
return true;
}
bool lander_post_paste_lsm(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
lander_ctx *c_ctx = ctx->c;
if (!lander_insert_entry(ctx)) {
conn->state = event_loop_conn_state_res;
return true;
}
lsm_entry_attr_insert_num(c_ctx->entry, lsm_attr_type_entry_type,
lander_entry_type_paste);
return true;
}
bool lander_stream_body_to_entry(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
lander_ctx *c_ctx = ctx->c;
http_loop_gctx *gctx = ctx->g;
lander_gctx *c_gctx = gctx->c;
uint64_t to_append =
MIN(conn->rbuf_size - conn->rbuf_read,
ctx->req.body.expected_len - lsm_entry_data_len(c_ctx->entry));
lsm_str *data;
lsm_str_init_copy_n(&data, (char *)&conn->rbuf[conn->rbuf_read], to_append);
lsm_entry_data_append(c_gctx->store, c_ctx->entry, data);
conn->rbuf_read += to_append;
lsm_str_free(data);
return lsm_entry_data_len(c_ctx->entry) == ctx->req.body.expected_len;
}
bool lander_post_redirect(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
bool random =
ctx->req.regex_groups[2].rm_eo == ctx->req.regex_groups[2].rm_so;
// Allocate a new buffer to pass to the trie
char *url = malloc(ctx->req.body.len + 1);
memcpy(url, ctx->req.body.buf, ctx->req.body.len);
url[ctx->req.body.len] = '\0';
Entry *new_entry = entry_new(Redirect, url);
// The entry duplicates the string
free(url);
// We don't check the result here, because we would perform the same action
// either way
char *key;
add_entry(&key, NULL, ctx, new_entry, random);
if (random) {
free(key);
}
conn->state = event_loop_conn_state_res;
return true;
}
bool lander_post_paste(event_loop_conn *conn) {
http_loop_ctx *ctx = conn->ctx;
lander_gctx *c_gctx = ctx->g->c;
bool random =
ctx->req.regex_groups[2].rm_eo == ctx->req.regex_groups[2].rm_so;
char *key;
int key_len;
Entry *new_entry = entry_new(Paste, "");
if (!add_entry(&key, &key_len, ctx, new_entry, random)) {
conn->state = event_loop_conn_state_res;
return true;
}
char *fname = malloc(strlen(c_gctx->data_dir) + 8 + key_len + 1);
sprintf(fname, "%s/pastes/%.*s", c_gctx->data_dir, key_len, key);
ctx->req.body.fname = fname;
ctx->req.body.fname_owned = true;
if (random) {
free(key);
}
return true;
}