aboutsummaryrefslogtreecommitdiff
path: root/src/clog.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/clog.c')
-rw-r--r--src/clog.c596
1 files changed, 334 insertions, 262 deletions
diff --git a/src/clog.c b/src/clog.c
index ba49ffc..077fba4 100644
--- a/src/clog.c
+++ b/src/clog.c
@@ -24,15 +24,16 @@ KORE_SECCOMP_FILTER("clog",
KORE_SYSCALL_ALLOW(uname)
)
-enum request_type { JSON, HTML };
+enum content_type { JSON, HTML };
-struct post_request {
- struct http_request *req;
- const char *resource;
- enum request_type type;
+enum query_status { QUERY_STATUS_OK, QUERY_STATUS_ERROR, QUERY_STATUS_NOT_FOUND };
- int resp_status;
- struct kore_buf *resp_buf;
+struct post_query {
+ const char *id;
+ enum content_type type;
+
+ int status;
+ struct kore_buf *result;
};
static const char *accept_json = "application/json";
@@ -44,66 +45,177 @@ static const char * const error_msg[] = {
[HTTP_STATUS_INTERNAL_ERROR] = "There was an error processing the request.",
};
-void post_request_init(struct post_request *post_req);
-void post_request_cleanup(struct post_request *post_req);
+void post_query_init(struct post_query *pq);
+void post_query_cleanup(struct post_query *pq);
+
+int validate_uuid(const char *uuid);
+
+enum content_type get_content_type(struct http_request *req);
+
+int http_ok_resp(
+ struct http_request *req,
+ enum content_type type,
+ enum http_status_code status,
+ struct kore_buf *result
+);
-int validate_uuid(const char *input);
+int http_err_resp(
+ struct http_request *req,
+ enum content_type type,
+ enum http_status_code status
+);
int redirect(struct http_request *req);
-int GET_post(struct http_request *req);
-int POST_post(struct http_request *req);
-int posts(struct http_request *req);
+int get_posts(struct http_request *req);
+int get_posts_resource(struct http_request *req);
+int post_posts(struct http_request *req);
-int render_posts(struct http_request *req, const char *resource);
-int query_posts(struct post_request *post_req);
+int sql_select_posts(struct post_query *pq);
+int sql_insert_posts(
+ const char *id,
+ const char *title,
+ const char *body
+);
+int sql_render_posts(struct kore_pgsql *sql, struct post_query *pq);
static void process_md_output(const MD_CHAR *, MD_SIZE size, void *);
static int render_md(const char *, struct kore_buf *);
void
-post_request_init(struct post_request *post_req) {
- post_req->req = NULL;
- post_req->resource = NULL;
- post_req->type = JSON;
+post_query_init(struct post_query *pq) {
+ pq->id = NULL;
+ pq->type = JSON;
- post_req->resp_status = HTTP_STATUS_OK;
- post_req->resp_buf = kore_buf_alloc(0);
+ pq->status = QUERY_STATUS_OK;
+ pq->result = kore_buf_alloc(0);
}
void
-post_request_cleanup(struct post_request *post_req) {
- if (post_req->resp_buf != NULL)
- kore_buf_free(post_req->resp_buf);
- post_req->resp_buf = NULL;
+post_query_cleanup(struct post_query *pq) {
+ if (pq->result != NULL)
+ kore_buf_free(pq->result);
+ pq->result = NULL;
}
int
-validate_uuid(const char *input) {
+validate_uuid(const char *uuid) {
int i = 0;
- const char *p = NULL;
- if (strlen(input) != 36)
+ if (strlen(uuid) != 36)
return KORE_RESULT_ERROR;
- for (i = 0, p = input; i <= 36; i++) {
+ for (i = 0; i <= 36; i++) {
if ((i == 8) || (i == 13) || (i == 18) || (i == 23)) {
- if (p[i] != '-')
+ if (uuid[i] != '-')
return KORE_RESULT_ERROR;
continue;
}
if (i == 36) {
- if (p[i] != '\0')
+ if (uuid[i] != '\0')
return KORE_RESULT_ERROR;
continue;
}
- if (!isxdigit(p[i]))
+ if (!isxdigit(uuid[i]))
return KORE_RESULT_ERROR;
}
return KORE_RESULT_OK;
}
+enum content_type
+get_content_type(struct http_request *req) {
+ int err = 0;
+
+ const char *accept = NULL;
+
+ err = http_request_header(req, "accept", &accept);
+ if (err == KORE_RESULT_OK) {
+ kore_log(LOG_DEBUG, "Accept: %s", accept);
+ if (strncmp(accept, accept_json, sizeof(*accept_json)) == 0)
+ return JSON;
+ }
+
+ return HTML;
+}
+
+int http_ok_resp(
+ struct http_request *req,
+ enum content_type type,
+ enum http_status_code status,
+ struct kore_buf *result
+) {
+ struct kore_buf *resp_buf = kore_buf_alloc(0);
+ const char *body = kore_buf_stringify(result, NULL);
+
+ if (type == JSON) {
+ http_response_header(req, "content-type", "application/json; charset=utf-8");
+ kore_buf_append(
+ resp_buf,
+ body,
+ strlen(body)
+ );
+ } else {
+ http_response_header(req, "content-type", "text/html; charset=utf-8");
+ kore_buf_append(resp_buf, asset_index_begin_html, asset_len_index_begin_html);
+ kore_buf_append(
+ resp_buf,
+ body,
+ strlen(body)
+ );
+ kore_buf_append(resp_buf, asset_index_end_html, asset_len_index_end_html);
+ }
+
+ http_response(
+ req,
+ status,
+ resp_buf->data,
+ resp_buf->offset
+ );
+
+ kore_buf_free(resp_buf);
+
+ return KORE_RESULT_OK;
+}
+
+int http_err_resp(
+ struct http_request *req,
+ enum content_type type,
+ enum http_status_code status
+) {
+ struct kore_buf *resp_buf = kore_buf_alloc(0);
+
+ if (type == JSON) {
+ http_response_header(req, "content-type", "application/json; charset=utf-8");
+ kore_buf_appendf(
+ resp_buf,
+ (const char *) asset_error_json,
+ error_msg[status]
+ );
+ } else {
+ http_response_header(req, "content-type", "text/html; charset=utf-8");
+ kore_buf_append(resp_buf, asset_index_begin_html, asset_len_index_begin_html);
+ kore_buf_appendf(
+ resp_buf,
+ (const char *) asset_error_html,
+ error_msg[status]
+ );
+ kore_buf_append(resp_buf, asset_index_end_html, asset_len_index_end_html);
+ }
+
+ http_response(
+ req,
+ status,
+ resp_buf->data,
+ resp_buf->offset
+ );
+
+ kore_buf_free(resp_buf);
+
+ return KORE_RESULT_OK;
+}
+
+
int
redirect(struct http_request *req) {
http_response_header(req, "Location", "/");
@@ -113,208 +225,209 @@ redirect(struct http_request *req) {
}
int
-GET_post(struct http_request *req) {
- const char *resource = NULL;
+get_posts(struct http_request *req) {
+ struct post_query pq;
- resource = req->path + strlen("/posts/");
- kore_log(LOG_DEBUG, "Resource /posts/%s", resource);
+ post_query_init(&pq);
+ pq.type = get_content_type(req);
- (void) render_posts(req, resource);
+ (void) sql_select_posts(&pq);
+ if (pq.status != QUERY_STATUS_OK)
+ http_err_resp(req, pq.type, HTTP_STATUS_INTERNAL_ERROR);
+ else
+ http_ok_resp(req, pq.type, HTTP_STATUS_OK, pq.result);
+
+ post_query_cleanup(&pq);
return KORE_RESULT_OK;
}
int
-posts(struct http_request *req) {
- if (req->method == HTTP_METHOD_GET)
- (void) render_posts(req, NULL);
- else if (req->method == HTTP_METHOD_POST)
- (void) POST_post(req);
+get_posts_resource(struct http_request *req) {
+ int err = 0;
+
+ struct post_query pq;
+
+ post_query_init(&pq);
+ pq.id = req->path + strlen("/posts/");
+ pq.type = get_content_type(req);
+
+ // Check for valid resource UUID
+ kore_log(LOG_DEBUG, "Resource id /posts/%s.", pq.id);
+ err = validate_uuid(pq.id);
+ if (err == KORE_RESULT_ERROR) {
+ kore_log(LOG_ERR, "Invalid post id %s.", pq.id);
+ http_err_resp(req, pq.type, HTTP_STATUS_NOT_FOUND);
+ goto out;
+ }
+
+ (void) sql_select_posts(&pq);
+ if (pq.status == QUERY_STATUS_NOT_FOUND)
+ http_err_resp(req, pq.type, HTTP_STATUS_NOT_FOUND);
+ else if (pq.status == QUERY_STATUS_ERROR)
+ http_err_resp(req, pq.type, HTTP_STATUS_INTERNAL_ERROR);
+ else
+ http_ok_resp(req, pq.type, HTTP_STATUS_OK, pq.result);
+
+out: ;
+
+ post_query_cleanup(&pq);
return KORE_RESULT_OK;
}
int
-render_posts(struct http_request *req, const char *resource) {
+post_posts(struct http_request *req) {
int err = 0;
- const char *accept = NULL;
+ int status = HTTP_STATUS_CREATED;
- struct post_request post_req;
+ enum content_type type = get_content_type(req);
- post_request_init(&post_req);
+ const char *id = NULL;
+ const char *title = NULL;
+ const char *body = NULL;
- post_req.req = req;
- post_req.resource = resource;
+ struct post_query pq;
- err = http_request_header(req, "accept", &accept);
- if (err == KORE_RESULT_OK) {
- kore_log(LOG_DEBUG, "Accept: %s", accept);
- if (strncmp(accept, accept_json, sizeof(*accept_json)) == 0)
- post_req.type = JSON;
- else
- post_req.type = HTML;
+ struct kore_json_item *item = NULL;
+ struct kore_json json;
+ kore_json_init(&json, req->http_body->data, req->http_body->length);
+
+ if (!kore_json_parse(&json)) {
+ status = HTTP_STATUS_BAD_REQUEST;
+ kore_log(LOG_ERR, "error parsing json: %s\n", kore_json_strerror());
+ goto out;
}
- if (post_req.type == JSON) {
- http_response_header(post_req.req, "content-type", "application/json; charset=utf-8");
-
- (void) query_posts(&post_req);
- // TOOD: Create a status_ok function
- if (post_req.resp_status != HTTP_STATUS_OK && post_req.resp_status != HTTP_STATUS_CREATED) {
- kore_buf_appendf(
- post_req.resp_buf,
- (const char *) asset_error_json,
- error_msg[post_req.resp_status]
- );
- }
+ item = kore_json_find_string(json.root, "id");
+ if (item != NULL) {
+ id = item->data.string;
+ kore_log(LOG_INFO, "id = '%s'\n", id);
+ } else {
+ status = HTTP_STATUS_BAD_REQUEST;
+ kore_log(LOG_ERR, "error parsing id: %s\n", kore_json_strerror());
+ goto out;
+ }
+
+ // Check for valid resource ID/UUID
+ err = validate_uuid(id);
+ if (err == KORE_RESULT_ERROR) {
+ status = HTTP_STATUS_BAD_REQUEST;
+ kore_log(LOG_ERR, "Invalid post UUID %s.", id);
+ goto out;
+ }
+ item = kore_json_find_string(json.root, "title");
+ if (item != NULL) {
+ title = item->data.string;
+ kore_log(LOG_INFO, "title = '%s'\n", title);
} else {
- http_response_header(post_req.req, "content-type", "text/html; charset=utf-8");
+ status = HTTP_STATUS_BAD_REQUEST;
+ kore_log(LOG_ERR, "error parsing title: %s\n", kore_json_strerror());
+ goto out;
+ }
- kore_buf_append(post_req.resp_buf, asset_index_begin_html, asset_len_index_begin_html);
+ item = kore_json_find_string(json.root, "body");
+ if (item != NULL) {
+ body = item->data.string;
+ kore_log(LOG_INFO, "body = '%s'\n", body);
+ } else {
+ status = HTTP_STATUS_BAD_REQUEST;
+ kore_log(LOG_ERR, "error parsing body: %s\n", kore_json_strerror());
+ goto out;
+ }
- (void) query_posts(&post_req);
- if (post_req.resp_status != HTTP_STATUS_OK && post_req.resp_status != HTTP_STATUS_CREATED) {
- kore_buf_appendf(
- post_req.resp_buf,
- (const char *) asset_error_html,
- error_msg[post_req.resp_status]
- );
- }
+ err = sql_insert_posts(id, title, body);
- kore_buf_append(post_req.resp_buf, asset_index_end_html, asset_len_index_end_html);
+ if (err == KORE_RESULT_ERROR) {
+ status = HTTP_STATUS_INTERNAL_ERROR;
+ goto out;
}
- http_response(
- post_req.req,
- post_req.resp_status,
- post_req.resp_buf->data,
- post_req.resp_buf->offset
- );
+out: ;
- post_request_cleanup(&post_req);
+ if (status == HTTP_STATUS_CREATED) {
+
+ post_query_init(&pq);
+ pq.id = id;
+ pq.type = type;
+
+ (void) sql_select_posts(&pq);
+ if (pq.status == QUERY_STATUS_NOT_FOUND)
+ http_err_resp(req, pq.type, HTTP_STATUS_NOT_FOUND);
+ else if (pq.status == QUERY_STATUS_ERROR)
+ http_err_resp(req, pq.type, HTTP_STATUS_INTERNAL_ERROR);
+ else
+ http_ok_resp(req, pq.type, HTTP_STATUS_OK, pq.result);
+
+ post_query_cleanup(&pq);
+ }
+ else
+ http_err_resp(req, type, status);
+
+ kore_json_cleanup(&json);
return KORE_RESULT_OK;
}
int
-query_posts(struct post_request *post_req) {
+sql_select_posts(struct post_query *pq) {
int err = 0;
- int row = 0, rows = 0;
-
- const char *id = NULL;
- const char *title = NULL;
- const char *created_at = NULL;
- const char *body = NULL;
-
- const char *json = NULL;
-
struct kore_pgsql sql;
kore_pgsql_init(&sql);
- // TODO use kore validation here.
- if (post_req->resource) {
- // Check for valid resource ID/UUID
- err = validate_uuid(post_req->resource);
- if (err == KORE_RESULT_ERROR) {
- post_req->resp_status = HTTP_STATUS_NOT_FOUND;
- kore_log(LOG_ERR, "Invalid post UUID %s.", post_req->resource);
- goto out;
- }
- }
+ pq->status = QUERY_STATUS_OK;
- // Initialize our kore_pgsql data structure with the database name
- // we want to connect to (note that we registered this earlier with
- // kore_pgsql_register()). We also say we will perform a synchronous
- // query (KORE_PGSQL_SYNC).
+ // Set up synchronous database handle.
err = kore_pgsql_setup(&sql, database, KORE_PGSQL_SYNC);
if (err == KORE_RESULT_ERROR) {
- post_req->resp_status = HTTP_STATUS_INTERNAL_ERROR;
+ pq->status = QUERY_STATUS_ERROR;
kore_pgsql_logerror(&sql);
goto out;
}
// Query for posts, check for error.
- if (post_req->resource != NULL) {
+ if (pq->id != NULL) {
// Query a post.
err = kore_pgsql_query_params(
&sql,
- post_req->type == HTML ? query_html_post : query_json_post,
+ pq->type == HTML ? query_html_post : query_json_post,
0, // return string data
1, // param count
- KORE_PGSQL_PARAM_TEXT(post_req->resource)
+ KORE_PGSQL_PARAM_TEXT(pq->id)
);
} else {
// Query all posts.
err = kore_pgsql_query(
&sql,
- post_req->type == HTML ? query_html_posts : query_json_posts
+ pq->type == HTML ? query_html_posts : query_json_posts
);
}
if (err == KORE_RESULT_ERROR) {
- post_req->resp_status = HTTP_STATUS_INTERNAL_ERROR;
+ pq->status = QUERY_STATUS_ERROR;
kore_pgsql_logerror(&sql);
goto out;
}
- if (post_req->type == JSON) {
- // XXX Always tuples from the above Postgres queries, need to check the length for results.
- if (kore_pgsql_getlength(&sql, 0, 0) == 0) {
- post_req->resp_status = HTTP_STATUS_NOT_FOUND;
- goto out;
- }
-
- json = kore_pgsql_getvalue(&sql, 0, 0);
- kore_buf_append(
- post_req->resp_buf,
- json,
- strlen(json)
- );
- } else { // post_req->type == HTML
- rows = kore_pgsql_ntuples(&sql);
- // TODO: Add test for this; When database is empty, don't return 404 for base request.
- if (post_req->resource && rows == 0) {
- post_req->resp_status = HTTP_STATUS_NOT_FOUND;
- goto out;
- }
+ // XXX Always tuples from the above Postgres queries, need to check the length for results.
+ if (pq->type == JSON && kore_pgsql_getlength(&sql, 0, 0) == 0) {
+ pq->status = QUERY_STATUS_NOT_FOUND;
+ goto out;
+ }
- // Iterate over posts and render them.
- for (row = 0; row < rows; row++) {
- id = kore_pgsql_getvalue(&sql, row, 0);
- title = kore_pgsql_getvalue(&sql, row, 1);
- created_at = kore_pgsql_getvalue(&sql, row, 2);
- body = kore_pgsql_getvalue(&sql, row, 3);
- kore_log(LOG_DEBUG, "id: '%s'; title '%s'", id, title);
-
- // Allocate a buffer to render the markdown as HTML into.
- struct kore_buf *html_buf = kore_buf_alloc(0);
-
- // Render MD.
- err = render_md(body, html_buf);
- if (err == KORE_RESULT_ERROR) {
- kore_log(LOG_ERR, "Error rendering markdown for entry %s.", id);
- kore_buf_free(html_buf);
- continue;
- }
-
- // Append rendered MD post.
- kore_buf_appendf(
- post_req->resp_buf,
- (const char *) asset_post_html,
- title,
- created_at,
- kore_buf_stringify(html_buf, NULL)
- );
-
- kore_buf_free(html_buf);
- }
+ // TODO: Add test for this; When database is empty, don't return 404 for base request.
+ if (pq->id != NULL && kore_pgsql_ntuples(&sql) == 0) {
+ pq->status = QUERY_STATUS_NOT_FOUND;
+ goto out;
}
+ (void) sql_render_posts(&sql, pq);
+
out: ;
kore_pgsql_cleanup(&sql);
@@ -322,78 +435,84 @@ out: ;
return KORE_RESULT_OK;
}
-int
-POST_post(struct http_request *req) {
+int sql_render_posts(struct kore_pgsql *sql, struct post_query *pq) {
int err = 0;
- int status = HTTP_STATUS_CREATED;
+ int row = 0, rows = 0;
- struct kore_json_item *item = NULL;
+ struct kore_buf *html_buf = NULL;
const char *id = NULL;
const char *title = NULL;
+ const char *created_at = NULL;
const char *body = NULL;
- struct kore_json json;
- struct kore_pgsql sql;
+ if (pq->type == JSON) {
+ kore_buf_append(
+ pq->result,
+ kore_pgsql_getvalue(sql, 0, 0),
+ kore_pgsql_getlength(sql, 0, 0)
+ );
+ return KORE_RESULT_OK;
+ }
- kore_json_init(&json, req->http_body->data, req->http_body->length);
+ // Allocate a buffer to render the markdown as HTML into.
+ html_buf = kore_buf_alloc(0);
+
+ // pg->type == HTML
+ // Iterate over posts and render them.
+ rows = kore_pgsql_ntuples(sql);
+ for (row = 0; row < rows; row++) {
+ // Reset buffer.
+ kore_buf_cleanup(html_buf);
+
+ // Fetch data.
+ id = kore_pgsql_getvalue(sql, row, 0);
+ title = kore_pgsql_getvalue(sql, row, 1);
+ created_at = kore_pgsql_getvalue(sql, row, 2);
+ body = kore_pgsql_getvalue(sql, row, 3);
+ kore_log(LOG_DEBUG, "id: '%s'; title '%s'", id, title);
+
+ // Render MD.
+ err = render_md(body, html_buf);
+ if (err == KORE_RESULT_ERROR) {
+ kore_log(LOG_ERR, "Error rendering markdown for entry %s.", id);
+ continue;
+ }
- kore_pgsql_init(&sql);
+ // Append rendered MD post.
+ kore_buf_appendf(
+ pq->result,
+ (const char *) asset_post_html,
+ title,
+ created_at,
+ kore_buf_stringify(html_buf, NULL)
+ );
- if (!kore_json_parse(&json)) {
- status = HTTP_STATUS_INTERNAL_ERROR;
- kore_log(LOG_ERR, "error parsing json: %s\n", kore_json_strerror());
- goto out;
}
- item = kore_json_find_string(json.root, "id");
- if (item != NULL) {
- id = item->data.string;
- kore_log(LOG_INFO, "id = '%s'\n", id);
- } else {
- status = HTTP_STATUS_INTERNAL_ERROR;
- kore_log(LOG_ERR, "error parsing id: %s\n", kore_json_strerror());
- goto out;
- }
+ kore_buf_free(html_buf);
- // Check for valid resource ID/UUID
- err = validate_uuid(id);
- if (err == KORE_RESULT_ERROR) {
- status = HTTP_STATUS_BAD_REQUEST;
- kore_log(LOG_ERR, "Invalid post UUID %s.", id);
- goto out;
- }
+ return KORE_RESULT_OK;
+}
- item = kore_json_find_string(json.root, "title");
- if (item != NULL) {
- title = item->data.string;
- kore_log(LOG_INFO, "title = '%s'\n", title);
- } else {
- status = HTTP_STATUS_INTERNAL_ERROR;
- kore_log(LOG_ERR, "error parsing title: %s\n", kore_json_strerror());
- goto out;
- }
+int
+sql_insert_posts(
+ const char *id,
+ const char *title,
+ const char *body
+) {
+ int err = KORE_RESULT_OK;
- item = kore_json_find_string(json.root, "body");
- if (item != NULL) {
- body = item->data.string;
- kore_log(LOG_INFO, "body = '%s'\n", body);
- } else {
- status = HTTP_STATUS_INTERNAL_ERROR;
- kore_log(LOG_ERR, "error parsing body: %s\n", kore_json_strerror());
- goto out;
- }
+ struct kore_pgsql sql;
+ kore_pgsql_init(&sql);
err = kore_pgsql_setup(&sql, database, KORE_PGSQL_SYNC);
if (err == KORE_RESULT_ERROR) {
- status = HTTP_STATUS_INTERNAL_ERROR;
kore_pgsql_logerror(&sql);
goto out;
}
- // Insert a post.
- // err = kore_pgsql_query(
err = kore_pgsql_query_params(
&sql,
"INSERT INTO posts (id, title, body) "
@@ -404,63 +523,16 @@ POST_post(struct http_request *req) {
KORE_PGSQL_PARAM_TEXT(title),
KORE_PGSQL_PARAM_TEXT(body)
);
-
if (err == KORE_RESULT_ERROR) {
- status = HTTP_STATUS_INTERNAL_ERROR;
kore_pgsql_logerror(&sql);
goto out;
}
out: ;
- if (status == HTTP_STATUS_CREATED)
- render_posts(req, id);
- else { // ERRORs
- int type = HTML;
- struct kore_buf *resp_buf = kore_buf_alloc(0);
- const char *accept = NULL;
-
- err = http_request_header(req, "accept", &accept);
- if (err == KORE_RESULT_OK) {
- kore_log(LOG_DEBUG, "Accept: %s", accept);
- if (strncmp(accept, accept_json, sizeof(*accept_json)) == 0)
- type = JSON;
- else
- type = HTML;
- }
-
- if (type == JSON) {
- http_response_header(req, "content-type", "application/json; charset=utf-8");
- kore_buf_appendf(
- resp_buf,
- (const char *) asset_error_json,
- error_msg[status]
- );
- } else {
- http_response_header(req, "content-type", "text/html; charset=utf-8");
- kore_buf_append(resp_buf, asset_index_begin_html, asset_len_index_begin_html);
- kore_buf_appendf(
- resp_buf,
- (const char *) asset_error_html,
- error_msg[status]
- );
- kore_buf_append(resp_buf, asset_index_end_html, asset_len_index_end_html);
- }
-
- http_response(
- req,
- status,
- resp_buf->data,
- resp_buf->offset
- );
-
- kore_buf_free(resp_buf);
- }
-
- kore_json_cleanup(&json);
kore_pgsql_cleanup(&sql);
- return KORE_RESULT_OK;
+ return err;
}
static int