#include #include #include #include #include "assets.h" #include "../lib/md4c/src/entity.h" #include "../lib/md4c/src/entity.c" #include "../lib/md4c/src/md4c.h" #include "../lib/md4c/src/md4c.c" #include "../lib/md4c/src/md4c-html.h" #include "../lib/md4c/src/md4c-html.c" KORE_SECCOMP_FILTER("clog", KORE_SYSCALL_ALLOW(bind), KORE_SYSCALL_ALLOW(getdents64), KORE_SYSCALL_ALLOW(newfstatat), KORE_SYSCALL_ALLOW(sendmmsg), KORE_SYSCALL_ALLOW(uname) ) struct post_request { struct http_request *req; const char *resource; // FIXME: use enum unsigned int type; // 0 JSON; 1 HTML int resp_status; struct kore_buf *resp_buf; }; void post_request_init(struct post_request *p); void post_request_cleanup(struct post_request *post_req); int redirect(struct http_request *); int post(struct http_request *); int posts(struct http_request *); int render_posts_html(struct post_request *); int render_posts_json(struct post_request *); static void process_md_output(const MD_CHAR *, MD_SIZE size, void *); static int render_md(const char *, struct kore_buf *); static const char *accept_json = "application/json"; static const char *database = "db"; static const char *req_err = "There was an error processing the request."; void post_request_init(struct post_request *post_req) { post_req->req = NULL; post_req->resource = NULL; post_req->type = 1; post_req->resp_status = HTTP_STATUS_OK; post_req->resp_buf = 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; } } int redirect(struct http_request *req) { http_response_header(req, "Location", "/"); http_response(req, HTTP_STATUS_MOVED_PERMANENTLY, NULL, 0); return KORE_RESULT_OK; } int post(struct http_request *req) { int err = 0; const char *accept = NULL; struct post_request post_req; post_request_init(&post_req); post_req.req = req; post_req.resource = req->path + strlen("/posts/"); kore_log(LOG_DEBUG, "Resource /posts/%s", post_req.resource); 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 = 0; } } if (post_req.type == 0) { http_response_header(post_req.req, "content-type", "application/json; charset=utf-8"); (void) render_posts_json(&post_req); } else { http_response_header(post_req.req, "content-type", "text/html; charset=utf-8"); (void) render_posts_html(&post_req); } http_response( post_req.req, post_req.resp_status, post_req.resp_buf->data, post_req.resp_buf->offset ); post_request_cleanup(&post_req); return KORE_RESULT_OK; } int posts(struct http_request *req) { int err = 0; const char *accept = NULL; struct post_request post_req; post_request_init(&post_req); post_req.req = req; 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 = 0; } } if (post_req.type == 0) { http_response_header(post_req.req, "content-type", "application/json; charset=utf-8"); err = render_posts_json(&post_req); } else { http_response_header(post_req.req, "content-type", "text/html; charset=utf-8"); err = render_posts_html(&post_req); } http_response( post_req.req, post_req.resp_status, post_req.resp_buf->data, post_req.resp_buf->offset ); post_request_cleanup(&post_req); return err; } int render_posts_html(struct post_request *post_req) { 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; struct kore_pgsql sql; kore_pgsql_init(&sql); kore_buf_append(post_req->resp_buf, asset_index_begin_html, asset_len_index_begin_html); // 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). err = kore_pgsql_setup(&sql, database, KORE_PGSQL_SYNC); if (err == KORE_RESULT_ERROR) { post_req->resp_status = HTTP_STATUS_INTERNAL_ERROR; kore_buf_appendf( post_req->resp_buf, (const char *) asset_error_html, req_err ); kore_pgsql_logerror(&sql); goto out; } // Query for posts, check for error. if (post_req->resource) { // Query a post. err = kore_pgsql_query_params( &sql, "SELECT id, title, created_at::DATE, body " "FROM posts " "WHERE id = $1;", 0, // return string data 1, // param count KORE_PGSQL_PARAM_TEXT(post_req->resource) ); } else { // Query all posts. err = kore_pgsql_query( &sql, "SELECT id, title, created_at::DATE, body " "FROM posts " "ORDER BY updated_at DESC;" ); } if (err == KORE_RESULT_ERROR) { post_req->resp_status = HTTP_STATUS_INTERNAL_ERROR; kore_buf_appendf( post_req->resp_buf, (const char *) asset_error_html, req_err ); kore_pgsql_logerror(&sql); goto out; } // Iterate over posts and render them. rows = kore_pgsql_ntuples(&sql); // if (rows == 0) ... 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); } out: ; kore_buf_append(post_req->resp_buf, asset_index_end_html, asset_len_index_end_html); kore_pgsql_cleanup(&sql); return KORE_RESULT_OK; } int render_posts_json(struct post_request *post_req) { int err = 0; int rows = 0; const char *json = NULL; struct kore_pgsql sql; kore_pgsql_init(&sql); err = kore_pgsql_setup(&sql, database, KORE_PGSQL_SYNC); if (err == KORE_RESULT_ERROR) { post_req->resp_status = HTTP_STATUS_INTERNAL_ERROR; kore_buf_appendf( post_req->resp_buf, (const char *) asset_error_json, req_err ); kore_pgsql_logerror(&sql); goto out; } if (post_req->resource) { err = kore_pgsql_query_params( &sql, "SELECT JSON_AGG(ROW_TO_JSON(row)) FROM (" "SELECT id, title, body, created_at, updated_at " "FROM posts " "WHERE id = $1" ") row;", 0, // string data 1, // param count KORE_PGSQL_PARAM_TEXT(post_req->resource) ); } else { err = kore_pgsql_query( &sql, "SELECT JSON_AGG(ROW_TO_JSON(row)) FROM (" "SELECT id, title, body, created_at, updated_at " "FROM posts " "ORDER BY updated_at DESC" ") row;" ); } if (err == KORE_RESULT_ERROR) { post_req->resp_status = HTTP_STATUS_INTERNAL_ERROR; kore_buf_appendf( post_req->resp_buf, (const char *) asset_error_json, req_err ); kore_pgsql_logerror(&sql); goto out; } rows = kore_pgsql_ntuples(&sql); if (rows == 1) { json = kore_pgsql_getvalue(&sql, 0, 0); kore_buf_append( post_req->resp_buf, json, strlen(json) ); } // else { out: ; kore_pgsql_cleanup(&sql); return KORE_RESULT_OK; } static int render_md(const char *in, struct kore_buf *out) { int err = 0; static unsigned parser_flags = 0; static unsigned renderer_flags = MD_HTML_FLAG_DEBUG; err = md_html( in, (MD_SIZE) strlen(in), process_md_output, (void*) out, parser_flags, renderer_flags ); if(err != 0) { kore_log(LOG_ERR, "Parsing Markdown failed.\n"); return KORE_RESULT_ERROR; } return KORE_RESULT_OK; } static void process_md_output(const MD_CHAR *html, MD_SIZE size, void *buf) { kore_buf_append((struct kore_buf *) buf, (const void *) html, (size_t) size); }