#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 struct post { time_t timestamp; User * user; char content[CONTENT_LEN]; }; // User struct user { char name[NAME_LEN]; Post * posts; User ** following; }; User UserNew(const char * name) { User result; strcpy(result.name, name); result.posts = NULL; result.following = NULL; return result; } void UserWrite(User * user) { char filename[128]; snprintf(filename, 128, "data/users/%s", user->name); FILE * f = fopen(filename, "w"); int numFollowing = arrlen(user->following); int numPosts = arrlen(user->posts); fwrite(&numFollowing, sizeof(int), 1, f); fwrite(&numPosts, sizeof(int), 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++) { fwrite(&user->posts[i].timestamp, sizeof(user->posts[i].timestamp), 1, f); fwrite(user->posts[i].user->name, 1, sizeof(user->posts[i].user->name), f); fwrite(user->posts[i].content, 1, sizeof(user->posts[i].content), f); } fclose(f); } void UserRead(User * user) { char filename[128]; snprintf(filename, 128, "data/users/%s", user->name); FILE * f = fopen(filename, "r"); int numFollowing; int numPosts; fread(&numFollowing, sizeof(int), 1, f); fread(&numPosts, sizeof(int), 1, f); for (int i = 0; i < numFollowing; i++) { char name[NAME_LEN]; fread(name, 1, sizeof(name), f); arrput(user->following, UsersFind(mg_str(name))); } for (int i = 0; i < numPosts; i++) { char name[NAME_LEN]; Post post; fread(&post.timestamp, sizeof(post.timestamp), 1, f); fread(name, 1, sizeof(name), f); post.user = UsersFind(mg_str(name)); fread(post.content, 1, sizeof(post.content), f); } fclose(f); } // Users User ** g_users; static User pat, yas, tof; void UsersSetup() { pat = UserNew("pat"); yas = UserNew("yas"); tof = UserNew("tof"); UserRead(&pat); UserRead(&yas); UserRead(&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_home(struct mg_connection * c, struct mg_http_message * hm, User * user) { 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; } char * html_post(Post * post) { static char html[2048]; snprintf(html, 2048, "

[%s] %s: %s

", ctime(&post->timestamp), post->user->name, post->user->name, post->content); return html; } 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); for (int i = 0; i < arrlen(posts); i++) { snprintf(html+strlen(html), HTML_LEN-strlen(html), html_post(posts[i])); } snprintf(html+strlen(html), HTML_LEN-strlen(html), "" ""); return html; } char * html_user(User * user) { static char html[HTML_LEN]; snprintf(html, HTML_LEN, "" "" "" "
" "
" "" "
", user->name); for (int i = arrlen(user->posts) - 1; i >= 0; i--) { snprintf(html+strlen(html), HTML_LEN-strlen(html), html_post(&user->posts[i])); } 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); struct mg_str caps[2]; if (mg_match(hm->uri, mg_str("/api/post/*"), caps)) { User * user = UsersFind(caps[0]); if (user != NULL) { Post newPost; newPost.timestamp = time(NULL); newPost.user = user; mg_http_get_var(&hm->body, "content", newPost.content, sizeof(newPost.content)-1); arrput(user->posts, newPost); UserWrite(user); http_redirect_home(c, hm, user); } } } // 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; }