#include #include #define STB_DS_IMPLEMENTATION #include #define NAME_LEN 32 #define CONTENT_LEN 1024 #define HTML_LEN 1024*8 typedef struct Post Post; typedef struct User User; User * UsersFind(struct mg_str name); // Post / User struct Post { time_t timestamp; User * user; char content[CONTENT_LEN]; uint32_t likes; Post * comments; //Post * parent; }; struct User { char name[NAME_LEN]; Post * posts; User ** following; }; Post PostNew() { Post result; memset(&result, 0, sizeof(result)); return result; } void PostWrite(Post * post, FILE * f) { fwrite(&post->timestamp, 8, 1, f); fwrite(post->user->name, 1, sizeof(post->user->name), f); fwrite(post->content, 1, sizeof(post->content), f); fwrite(&post->likes, 4, 1, f); uint32_t numComments = arrlen(post->comments); fwrite(&numComments, 4, 1, f); for (int i = 0; i < numComments; i++) { PostWrite(&post->comments[i], f); } } void PostRead(Post * post, FILE * f) { fread(&post->timestamp, 8, 1, f); char name[NAME_LEN]; fread(name, 1, sizeof(name), f); post->user = UsersFind(mg_str(name)); fread(post->content, 1, sizeof(post->content), f); fread(&post->likes, 4, 1, f); uint32_t numComments; fread(&numComments, 4, 1, f); post->comments = NULL; for (int i = 0; i < numComments; i++) { Post newComment = PostNew(); PostRead(&newComment, f); arrput(post->comments, newComment); } } User UserNew(const char * name) { User result; memset(&result, 0, sizeof(result)); strcpy(result.name, name); return result; } void UserWrite(User * user, FILE * f) { fwrite(user->name, 1, sizeof(user->name), f); uint32_t numFollowing = arrlen(user->following); uint32_t numPosts = arrlen(user->posts); fwrite(&numFollowing, 4, 1, f); fwrite(&numPosts, 4, 1, f); for (int i = 0; i < numFollowing; i++) { fwrite(user->following[i]->name, 1, sizeof(user->following[i]->name), f); } for (int i = 0; i < numPosts; i++) { PostWrite(&user->posts[i], f); } } void UserRead(User * user, FILE * f) { fread(user->name, 1, sizeof(user->name), f); uint32_t numFollowing; uint32_t numPosts; fread(&numFollowing, 1, 4, f); fread(&numPosts, 1, 4, f); user->following = NULL; for (int i = 0; i < numFollowing; i++) { char name[NAME_LEN]; fread(name, 1, sizeof(name), f); arrput(user->following, UsersFind(mg_str(name))); } user->posts = NULL; for (int i = 0; i < numPosts; i++) { Post post = PostNew(); PostRead(&post, f); arrput(user->posts, post); } } // Users User ** g_users; static User pat, yas, tof; void UsersSetup() { pat = UserNew("pat"); yas = UserNew("yas"); tof = UserNew("tof"); arrput(pat.following, &yas); arrput(yas.following, &pat); arrput(tof.following, &yas); arrput(tof.following, &pat); arrput(g_users, &pat); arrput(g_users, &yas); arrput(g_users, &tof); } User * UsersFind(struct mg_str name) { for (int i = 0; i < arrlen(g_users); i++) { User * user = g_users[i]; if (strncmp(user->name, name.ptr, name.len) == 0) return user; } return NULL; } // HTML void http_redirect(struct mg_connection * c, struct mg_http_message * hm) { struct mg_str *referer = mg_http_get_header(hm, "Referer"); static char redirectHeaders[128]; snprintf(redirectHeaders, 128, "Location: %.*s\r\n", referer->len, referer->ptr); mg_http_reply(c, 303, redirectHeaders, ""); } char * html_404() { static char html[1024]; snprintf(html, 1024, "" "" "" "

Eeeeeeh? The Website's Under Construction

" "" ""); return html; } char * html_user_not_found() { static char html[1024]; snprintf(html, 1024, "" "" "" "

User was not found :/

" "" ""); return html; } int html_post(Post * post, char * buffer, int bufferLen) { int result = 0; int printed = 0; printed = snprintf(buffer, bufferLen, "
[%s] %s: %s
", ctime(&post->timestamp), post->user->name, post->user->name, post->content); buffer += printed; bufferLen -= printed; result += printed; if (post->likes > 0) { printed = snprintf(buffer, bufferLen, "Likes: %d
", post->likes); buffer += printed; bufferLen -= printed; result += printed; } printed = snprintf(buffer, bufferLen, "
" "" "
", post->user->name, (size_t)post); buffer += printed; bufferLen -= printed; result += printed; if (arrlen(post->comments) > 0) { printed = snprintf(buffer, bufferLen, "Comments:
"); buffer += printed; bufferLen -= printed; result += printed; for (int i = 0; i < arrlen(post->comments); i++) { printed = html_post(&post->comments[i], buffer, bufferLen); buffer += printed; bufferLen -= printed; result += printed; } } printed = snprintf(buffer, bufferLen, "
" "" "" "
" "
", post->user->name, (size_t)post); buffer += printed; bufferLen -= printed; result += printed; return result; } char * html_home(User * user) { Post ** posts = NULL; for (int i = 0; i < arrlen(user->following); i++) { User * userF = user->following[i]; for (int j = 0; j < arrlen(userF->posts); j++) { Post * post = &userF->posts[j]; bool inserted = false; for (int k = 0; k < arrlen(posts); k++) { if (post->timestamp > posts[k]->timestamp) { arrins(posts, k, post); inserted = true; break; } } if (! inserted) arrput(posts, post); } } static char html[HTML_LEN]; snprintf(html, HTML_LEN, "" "" "" "
" "
" "" "
", user->name); snprintf(html+strlen(html), HTML_LEN-strlen(html), "
" "" "
" "
" "" "
", user->name, user->name); for (int i = 0; i < arrlen(posts); i++) { html_post(posts[i], html+strlen(html), HTML_LEN-strlen(html)); } snprintf(html+strlen(html), HTML_LEN-strlen(html), "" ""); arrfree(posts); return html; } char * html_user(User * user) { static char html[HTML_LEN]; snprintf(html, HTML_LEN, "" "" "" "
" "
" "" "
", user->name); snprintf(html+strlen(html), HTML_LEN-strlen(html), "
" "" "
" "
" "" "
", user->name, user->name); for (int i = arrlen(user->posts) - 1; i >= 0; i--) { html_post(&user->posts[i], html+strlen(html), HTML_LEN-strlen(html)); } snprintf(html+strlen(html), HTML_LEN-strlen(html), "" ""); return html; } static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { if (ev == MG_EV_HTTP_MSG) { struct mg_http_message *hm = (struct mg_http_message *)ev_data; // POST if (mg_strcmp(hm->method, mg_str("POST")) == 0) { // printf("POST: %.*s %.*s\n", // hm->uri.len, hm->uri.ptr, // hm->body.len, hm->body.ptr); // printf("Message: %.*s\n", // hm->message.len, hm->message.ptr); // TODO: make sure this diesnt error. shhhhhh! struct mg_str caps[10]; if (mg_match(hm->uri, mg_str("/api/post/*"), caps)) { User * user = UsersFind(caps[0]); if (user != NULL) { Post newPost; newPost.likes = 0; newPost.comments = NULL; newPost.timestamp = time(NULL); newPost.user = user; mg_http_get_var(&hm->body, "content", newPost.content, sizeof(newPost.content)-1); arrput(user->posts, newPost); } } else if (mg_match(hm->uri, mg_str("/api/comment/*/*"), caps)) { User * user = UsersFind(caps[0]); if (user != NULL) { Post * parent = (Post *)atoll(caps[1].ptr); if (parent != NULL) { Post newComment; newComment.likes = 0; newComment.comments = NULL; newComment.timestamp = time(NULL); newComment.user = user; mg_http_get_var(&hm->body, "content", newComment.content, sizeof(newComment.content)-1); arrput(parent->comments, newComment); } } } else if (mg_match(hm->uri, mg_str("/api/like/*/*"), caps)) { User * user = UsersFind(caps[0]); if (user != NULL) { Post * post = (Post *)atoll(caps[1].ptr); if (post != NULL) post->likes++; } } else if (mg_match(hm->uri, mg_str("/api/write/*"), caps)) { User * user = UsersFind(caps[0]); if (user != NULL) { char filename[128]; snprintf(filename, 128, "data/users/%s", user->name); FILE * f = fopen(filename, "w"); UserWrite(user, f); fclose(f); } } else if (mg_match(hm->uri, mg_str("/api/read/*"), caps)) { User * user = UsersFind(caps[0]); if (user != NULL) { char filename[128]; snprintf(filename, 128, "data/users/%s", user->name); FILE * f = fopen(filename, "r"); UserRead(user, f); fclose(f); } } http_redirect(c, hm); } // GET else if (mg_strcmp(hm->method, mg_str("GET")) == 0) { struct mg_str caps[2]; if (mg_match(hm->uri, mg_str("/home/*"), caps)) { User * user = UsersFind(caps[0]); if (user == NULL) mg_http_reply(c, 200, "", html_user_not_found()); else mg_http_reply(c, 200, "", html_home(user)); } else if (mg_match(hm->uri, mg_str("/user/*"), caps)) { User * user = UsersFind(caps[0]); if (user == NULL) mg_http_reply(c, 200, "", html_user_not_found()); else mg_http_reply(c, 200, "", html_user(user)); } else { mg_http_reply(c, 404, "", html_404()); } } else { mg_http_reply(c, 404, "", "Unknown"); } } } int main(int argc, char *argv[]) { UsersSetup(); struct mg_mgr mgr; mg_mgr_init(&mgr); mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr); while (1) mg_mgr_poll(&mgr, 1000); mg_mgr_free(&mgr); return 0; }