#include "cweb.h" #if 0 #define WL_STR(X) ((WL_String) { (X), (int) sizeof(X)-1}) #define HTML_STR(X) html_str(HTTP_STR(#X)) static CWEB_String html_str(CWEB_String str) { str = cweb_trim(str); if (str.len > 0 && str.ptr[0] == '(') { str.ptr++; str.len--; } if (str.len > 0 && str.ptr[str.len-1] == ')') str.len--; return str; } int load_file(char *file, char **data, long *size) { FILE *f = fopen(file, "rb"); if (f == NULL) return -1; fseek(f, 0, SEEK_END); *size = ftell(f); fseek(f, 0, SEEK_SET); *data = malloc(*size + 1); fread(*data, 1, *size, f); (*data)[*size] = '\0'; fclose(f); return 0; } static void *alloc(WL_Arena *arena, int num, int align) { int pad = -(uintptr_t) (arena->ptr + arena->cur) & (align-1); if (arena->len - arena->cur < num + pad) return NULL; void *ptr = arena->ptr + arena->cur + pad; arena->cur += num + pad; return ptr; } static bool is_hex_digit(char c) { return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); } static int hex_digit_to_int(char c) { if (c >= 'A' && c <= 'F') return c - 'A' + 10; if (c >= 'a' && c <= 'f') return c - 'a' + 10; return c - '0'; } #endif #define USERNAME_LIMIT 64 bool valid_name(CWEB_String str) { (void) str; // TODO return true; } bool valid_email(CWEB_String str) { (void) str; // TODO return true; } bool valid_pass(CWEB_String str) { (void) str; // TODO return true; } bool valid_post_title(CWEB_String str) { (void) str; // TODO return true; } bool valid_post_content(CWEB_String str) { (void) str; // TODO return true; } bool valid_link(CWEB_String str) { (void) str; return true; } bool valid_comment_content(CWEB_String str) { (void) str; return true; } // TODO: check for the correct methods at each endpoint static int user_exists(CWEB *cweb, CWEB_String name, CWEB_String pass) { CWEB_QueryResult res = cweb_database_select(cweb, "SELECT id, hash FROM Users WHERE username=?", name); int64_t user_id; CWEB_PasswordHash hash; int ret = cweb_next_query_row(&res, &user_id, &hash); if (ret < 0) { cweb_free_query_result(&res); return -1; } if (ret == 0) { cweb_free_query_result(&res); return 0; } ret = cweb_check_password(pass.ptr, pass.len, hash); if (ret < 0) { cweb_free_query_result(&res); return -500; } if (ret > 0) { cweb_free_query_result(&res); return -400; } return user_id; } static void endpoint_api_login(CWEB *cweb, CWEB_Request *req) { if (cweb_get_session_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); return; } CWEB_String name = cweb_get_param_s(req, CWEB_STR("username")); CWEB_String pass = cweb_get_param_s(req, CWEB_STR("password")); if (!valid_name(name) || !valid_pass(pass)) { cweb_respond_basic(req, 200, CWEB_STR("
Invalid credentials
")); return; } int ret = user_exists(cweb, name, pass); if (ret < 0) { cweb_respond_basic(req, 200, CWEB_STR("
Invalid credentials
")); return; } if (cweb_set_session_user_id(req, ret) < 0) { // TODO } cweb_respond_redirect(req, CWEB_STR("/index")); } static void endpoint_api_signup(CWEB *cweb, CWEB_Request *req) { if (cweb_get_session_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); return; } CWEB_String name = cweb_get_param_s(req, CWEB_STR("username")); CWEB_String email = cweb_get_param_s(req, CWEB_STR("email")); CWEB_String pass1 = cweb_get_param_s(req, CWEB_STR("password1")); CWEB_String pass2 = cweb_get_param_s(req, CWEB_STR("password2")); if (!valid_name(name) || !valid_email(email) || !valid_pass(pass1)) { cweb_respond_basic(req, 200, CWEB_STR("
Invalid credentials
")); return; } if (!cweb_streq(pass1, pass2)) { cweb_respond_basic(req, 200, CWEB_STR("
The password was repeated incorrectly
")); return; } CWEB_PasswordHash hash; int ret = cweb_hash_password(pass1.ptr, pass1.len, 12, &hash); if (ret) { cweb_respond_basic(req, 400, CWEB_STR("
Internal error
")); return; } int64_t insert_id = cweb_database_insert(cweb, "INSERT INTO Users(username, email, hash) VALUES (?, ?, ?)", name, email, hash); if (insert_id < 0) { // TODO: What if the user exists? cweb_respond_basic(req, 400, CWEB_STR("
Internal error
")); return; } cweb_set_session_user_id(req, insert_id); cweb_respond_redirect(req, CWEB_STR("/index")); } static void endpoint_api_logout(CWEB *cweb, CWEB_Request *req) { cweb_set_session_user_id(req, -1); cweb_respond_redirect(req, CWEB_STR("/index")); } static void endpoint_api_post(CWEB *cweb, CWEB_Request *req) { int user_id = cweb_get_session_user_id(req); if (user_id == -1) { cweb_respond_redirect(req, CWEB_STR("/index")); return; } CWEB_String title = cweb_get_param_s(req, CWEB_STR("title")); CWEB_String link = cweb_get_param_s(req, CWEB_STR("link")); CWEB_String content = cweb_get_param_s(req, CWEB_STR("content")); CWEB_String csrf = cweb_get_param_s(req, CWEB_STR("csrf")); if (!cweb_streq(cweb_get_session_csrf(req), csrf)) { cweb_respond_basic(req, 400, CWEB_STR("Invalid request")); return; } if (!valid_post_title(title)) { cweb_respond_basic(req, 200, CWEB_STR("
Invalid title
")); return; } if (!valid_link(link)) { cweb_respond_basic(req, 200, CWEB_STR("
Invalid link
")); return; } if (!valid_post_content(content)) { cweb_respond_basic(req, 200, CWEB_STR("
Invalid content
")); return; } bool is_link = false; if (link.len > 0) { is_link = true; content = link; } int64_t insert_id = cweb_database_insert(cweb, "INSERT INTO Posts(author, title, is_link, content) VALUES (?, ?, ?, ?)", user_id, title, is_link, content); if (insert_id < 0) { cweb_respond_basic(req, 500, CWEB_STR("
Internal error
")); return; } int post_id = (int) insert_id; cweb_respond_redirect(req, CWEB_STR("/post?id={}"), post_id); } static void endpoint_api_comment(CWEB *cweb, CWEB_Request *req) { int user_id = cweb_get_session_user_id(req); if (user_id == -1) { cweb_respond_basic(req, 200, CWEB_STR("
You are not logged in
")); return; } int parent_post = cweb_get_param_i(req, CWEB_STR("parent_post")); int parent_comment = cweb_get_param_i(req, CWEB_STR("parent_comment")); CWEB_String content = cweb_get_param_s(req, CWEB_STR("content")); CWEB_String csrf2 = cweb_get_param_s(req, CWEB_STR("csrf")); if (!cweb_streq(cweb_get_session_csrf(req), csrf2)) { cweb_respond_basic(req, 200, CWEB_STR("
Invalid request
")); return; } content = cweb_trim(content); if (!valid_comment_content(content)) { cweb_respond_basic(req, 200, CWEB_STR("
Invalid content
")); return; } int64_t insert_id; if (parent_comment == -1) insert_id = cweb_database_insert(cweb, "INSERT INTO Comments(author, content, parent_post) VALUES (?, ?, ?)", user_id, content, parent_post); else insert_id = cweb_database_insert(cweb, "INSERT INTO Comments(author, content, parent_post, parent_comment) VALUES (?, ?, ?, ?)", user_id, content, parent_post, parent_comment); if (insert_id < 0) { cweb_respond_basic(req, 200, CWEB_STR("
Internal error
")); return; } cweb_respond_redirect(req, CWEB_STR("/post?id={}"), parent_post); } static void endpoint_index(CWEB *cweb, CWEB_Request *req) { cweb_respond_template(req, 200, CWEB_STR("pages/index.wl"), -1); } static void endpoint_write(CWEB *cweb, CWEB_Request *req) { if (cweb_get_session_user_id(req) == -1) { cweb_respond_redirect(req, CWEB_STR("/index")); return; } cweb_respond_template(req, 200, CWEB_STR("pages/write.wl"), -1); } static void endpoint_login(CWEB *cweb, CWEB_Request *req) { if (cweb_get_session_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); return; } cweb_respond_template(req, 200, CWEB_STR("pages/login.wl"), -1); } static void endpoint_signup(CWEB *cweb, CWEB_Request *req) { if (cweb_get_session_user_id(req) != -1) { cweb_respond_redirect(req, CWEB_STR("/index")); return; } cweb_respond_template(req, 200, CWEB_STR("pages/signup.wl"), -1); } static void endpoint_post(CWEB *cweb, CWEB_Request *req) { int post_id = cweb_get_param_i(req, CWEB_STR("id")); if (post_id < 0) { cweb_respond_template(req, 404, CWEB_STR("pages/notfound.wl"), -1); return; } CWEB_QueryResult res = cweb_database_select(cweb, "SELECT COUNT(*) FROM Posts WHERE id=?", post_id); int num; if (cweb_next_query_row(&res, &num) != 1) { cweb_respond_basic(req, 500, CWEB_STR("")); cweb_free_query_result(&res); return; } cweb_free_query_result(&res); if (num < 0) { cweb_respond_basic(req, 500, CWEB_STR("")); return; } if (num == 0) { cweb_respond_template(req, 404, CWEB_STR("pages/notfound.wl"), -1); return; } cweb_respond_template(req, 200, CWEB_STR("pages/post.wl"), post_id); } static void endpoint_fallback(CWEB *cweb, CWEB_Request *req) { cweb_respond_template(req, 404, CWEB_STR("pages/notfound.wl"), -1); } int main(void) { CWEB_String addr = CWEB_STR("127.0.0.1"); uint16_t port = 8080; if (cweb_global_init() < 0) return -1; CWEB *cweb = cweb_init(addr, port); if (cweb == NULL) { cweb_global_free(); return -1; } for (;;) { CWEB_Request *req = cweb_wait(cweb); if (0) {} else if (cweb_match_endpoint(req, CWEB_STR("/api/login"))) endpoint_api_login(cweb, req); else if (cweb_match_endpoint(req, CWEB_STR("/api/signup"))) endpoint_api_signup(cweb, req); else if (cweb_match_endpoint(req, CWEB_STR("/api/logout"))) endpoint_api_logout(cweb, req); else if (cweb_match_endpoint(req, CWEB_STR("/api/post"))) endpoint_api_post(cweb, req); else if (cweb_match_endpoint(req, CWEB_STR("/api/comment"))) endpoint_api_comment(cweb, req); else if (cweb_match_endpoint(req, CWEB_STR("/index"))) endpoint_index(cweb, req); else if (cweb_match_endpoint(req, CWEB_STR("/write"))) endpoint_write(cweb, req); else if (cweb_match_endpoint(req, CWEB_STR("/login"))) endpoint_login(cweb, req); else if (cweb_match_endpoint(req, CWEB_STR("/signup"))) endpoint_signup(cweb, req); else if (cweb_match_endpoint(req, CWEB_STR("/post"))) endpoint_post(cweb, req); else endpoint_fallback(cweb, req); } cweb_free(cweb); cweb_global_free(); return 0; }