-#include "mongoose.h"\r
+#include <stdint.h>\r
+#include <mongoose.h>\r
+#define STB_DS_IMPLEMENTATION\r
+#include <stb_ds.h>\r
+\r
+\r
+#define NAME_LEN 32\r
+#define CONTENT_LEN 1024\r
+#define HTML_LEN 1024*8\r
+\r
+typedef struct Post Post;\r
+typedef struct User User;\r
+\r
+User * UsersFind(struct mg_str name);\r
+\r
+// Post / User\r
+\r
+struct Post {\r
+ time_t timestamp;\r
+ User * user;\r
+ char content[CONTENT_LEN];\r
+ uint32_t likes;\r
+ Post * comments;\r
+ //Post * parent;\r
+};\r
+\r
+struct User {\r
+ char name[NAME_LEN];\r
+ Post * posts;\r
+ User ** following;\r
+};\r
+\r
+Post\r
+PostNew() {\r
+ Post result;\r
+ memset(&result, 0, sizeof(result));\r
+ return result;\r
+}\r
+\r
+void\r
+PostWrite(Post * post, FILE * f) {\r
+ fwrite(&post->timestamp, 8, 1, f);\r
+ fwrite(post->user->name, 1, sizeof(post->user->name), f);\r
+ fwrite(post->content, 1, sizeof(post->content), f);\r
+ fwrite(&post->likes, 4, 1, f);\r
+ uint32_t numComments = arrlen(post->comments);\r
+ fwrite(&numComments, 4, 1, f);\r
+ for (int i = 0; i < numComments; i++) {\r
+ PostWrite(&post->comments[i], f);\r
+ }\r
+}\r
+\r
+void\r
+PostRead(Post * post, FILE * f) {\r
+ fread(&post->timestamp, 8, 1, f);\r
+ char name[NAME_LEN];\r
+ fread(name, 1, sizeof(name), f);\r
+ post->user = UsersFind(mg_str(name));\r
+ fread(post->content, 1, sizeof(post->content), f);\r
+ fread(&post->likes, 4, 1, f);\r
+ uint32_t numComments;\r
+ fread(&numComments, 4, 1, f);\r
+ post->comments = NULL;\r
+ for (int i = 0; i < numComments; i++) {\r
+ Post newComment = PostNew();\r
+ PostRead(&newComment, f);\r
+ arrput(post->comments, newComment);\r
+ }\r
+}\r
+\r
+User\r
+UserNew(const char * name) {\r
+ User result;\r
+ memset(&result, 0, sizeof(result));\r
+ strcpy(result.name, name);\r
+ return result;\r
+}\r
+\r
+void\r
+UserWrite(User * user, FILE * f) {\r
+ fwrite(user->name, 1, sizeof(user->name), f);\r
+\r
+ uint32_t numFollowing = arrlen(user->following);\r
+ uint32_t numPosts = arrlen(user->posts);\r
+ fwrite(&numFollowing, 4, 1, f);\r
+ fwrite(&numPosts, 4, 1, f);\r
+\r
+ for (int i = 0; i < numFollowing; i++) {\r
+ fwrite(user->following[i]->name, 1, sizeof(user->following[i]->name), f);\r
+ }\r
+\r
+ for (int i = 0; i < numPosts; i++) {\r
+ PostWrite(&user->posts[i], f);\r
+ }\r
+}\r
+\r
+void\r
+UserRead(User * user, FILE * f) {\r
+ fread(user->name, 1, sizeof(user->name), f);\r
+\r
+ uint32_t numFollowing;\r
+ uint32_t numPosts;\r
+ fread(&numFollowing, 1, 4, f);\r
+ fread(&numPosts, 1, 4, f);\r
+\r
+ user->following = NULL;\r
+ for (int i = 0; i < numFollowing; i++) {\r
+ char name[NAME_LEN];\r
+ fread(name, 1, sizeof(name), f);\r
+ arrput(user->following, UsersFind(mg_str(name)));\r
+ }\r
+\r
+ user->posts = NULL;\r
+ for (int i = 0; i < numPosts; i++) {\r
+ Post post = PostNew();\r
+ PostRead(&post, f);\r
+ arrput(user->posts, post);\r
+ }\r
+}\r
+\r
+\r
+\r
+// Users\r
+\r
+User ** g_users;\r
+static User pat, yas, tof;\r
+\r
+void\r
+UsersSetup() {\r
+ pat = UserNew("pat");\r
+ yas = UserNew("yas");\r
+ tof = UserNew("tof");\r
+\r
+ arrput(pat.following, &yas);\r
+ arrput(yas.following, &pat);\r
+ arrput(tof.following, &yas);\r
+ arrput(tof.following, &pat);\r
+\r
+ arrput(g_users, &pat);\r
+ arrput(g_users, &yas);\r
+ arrput(g_users, &tof);\r
+}\r
+\r
+User *\r
+UsersFind(struct mg_str name) {\r
+ for (int i = 0; i < arrlen(g_users); i++) {\r
+ User * user = g_users[i];\r
+ if (strncmp(user->name, name.ptr, name.len) == 0)\r
+ return user;\r
+ }\r
+ return NULL;\r
+}\r
+\r
\r
\r
// HTML\r
\r
+void\r
+http_redirect(struct mg_connection * c, struct mg_http_message * hm) {\r
+ struct mg_str *referer = mg_http_get_header(hm, "Referer");\r
+\r
+ static char redirectHeaders[128];\r
+ snprintf(redirectHeaders, 128,\r
+ "Location: %.*s\r\n",\r
+ referer->len, referer->ptr);\r
+ mg_http_reply(c, 303, redirectHeaders, "");\r
+}\r
+\r
char *\r
-html_user_post(struct mg_str user) {\r
- static char html[1024];\r
- snprintf(html, 1024,\r
- "<!DOCTYPE html>"\r
- "<html>"\r
- "<body>"\r
- "<form action=\"/user/%.*s/post\" method=\"post\">"\r
- " <input type=\"text\" id=\"content\" name=\"content\"><br>"\r
- " <input type=\"submit\" value=\"Chirp!\">"\r
- "</form>"\r
- "</body>"\r
- "</html>",\r
- user.len, user.ptr);\r
- return html;\r
+html_404()\r
+{\r
+ static char html[1024];\r
+ snprintf(html, 1024,\r
+ "<!DOCTYPE html>"\r
+ "<html>"\r
+ "<body>"\r
+ "<p><i>Eeeeeeh?</i> <b>The Website's Under Construction</b></p>"\r
+ "</body>"\r
+ "</html>");\r
+ return html;\r
}\r
\r
-// Post\r
+char *\r
+html_user_not_found()\r
+{\r
+ static char html[1024];\r
+ snprintf(html, 1024,\r
+ "<!DOCTYPE html>"\r
+ "<html>"\r
+ "<body>"\r
+ "<p>User was not found :/</p>"\r
+ "</body>"\r
+ "</html>");\r
+ return html;\r
+}\r
\r
+int\r
+html_post(Post * post, char * buffer, int bufferLen) {\r
+ int result = 0;\r
+ int printed = 0;\r
\r
+ printed = snprintf(buffer, bufferLen,\r
+ "<div style=\"border: 2px solid black; margin: 2px; padding: 2px;\">[%s] <a href=\"/user/%s\">%s</a>: %s <br />",\r
+ ctime(&post->timestamp),\r
+ post->user->name,\r
+ post->user->name,\r
+ post->content);\r
+ buffer += printed; bufferLen -= printed; result += printed;\r
\r
+ if (post->likes > 0) {\r
+ printed = snprintf(buffer, bufferLen,\r
+ "Likes: %d<br />", post->likes);\r
+ buffer += printed; bufferLen -= printed; result += printed;\r
+ }\r
\r
-// User\r
+ printed = snprintf(buffer, bufferLen,\r
+ "<form action=\"/api/like/%s/%llu\" method=\"post\">"\r
+ "<input type=\"submit\" value=\"Like!\">"\r
+ "</form>",\r
+ post->user->name,\r
+ (size_t)post);\r
+ buffer += printed; bufferLen -= printed; result += printed;\r
+ \r
+ if (arrlen(post->comments) > 0) {\r
+ printed = snprintf(buffer, bufferLen,\r
+ "Comments:<br />");\r
+ buffer += printed; bufferLen -= printed; result += printed;\r
\r
+ for (int i = 0; i < arrlen(post->comments); i++) {\r
+ printed = html_post(&post->comments[i], buffer, bufferLen);\r
+ buffer += printed; bufferLen -= printed; result += printed;\r
+ }\r
+ }\r
\r
+ printed = snprintf(buffer, bufferLen,\r
+ "<form action=\"/api/comment/%s/%llu\" method=\"post\">"\r
+ "<input type=\"text\" name=\"content\" autofocus>"\r
+ "<input type=\"submit\" value=\"Comment!\">"\r
+ "</form>"\r
+ "</div>",\r
+ post->user->name,\r
+ (size_t)post);\r
+ buffer += printed; bufferLen -= printed; result += printed;\r
\r
+ return result;\r
+}\r
\r
+char *\r
+html_home(User * user)\r
+{\r
+ Post ** posts = NULL;\r
+ for (int i = 0; i < arrlen(user->following); i++) {\r
+ User * userF = user->following[i];\r
+ for (int j = 0; j < arrlen(userF->posts); j++) {\r
+ Post * post = &userF->posts[j];\r
+ bool inserted = false;\r
+ for (int k = 0; k < arrlen(posts); k++) {\r
+ if (post->timestamp > posts[k]->timestamp) {\r
+ arrins(posts, k, post);\r
+ inserted = true;\r
+ break;\r
+ }\r
+ }\r
+ if (! inserted)\r
+ arrput(posts, post);\r
+ }\r
+ }\r
+\r
+ static char html[HTML_LEN];\r
+ snprintf(html, HTML_LEN,\r
+ "<!DOCTYPE html>"\r
+ "<html>"\r
+ "<body>"\r
+ "<form action=\"/api/post/%s\" method=\"post\">"\r
+ "<input type=\"text\" name=\"content\" autofocus><br>"\r
+ "<input type=\"submit\" value=\"Chirp!\">"\r
+ "</form>",\r
+ user->name);\r
+ snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
+ "<form action=\"/api/write/%s\" method=\"post\">"\r
+ "<input type=\"submit\" value=\"Write!\">"\r
+ "</form>"\r
+ "<form action=\"/api/read/%s\" method=\"post\">"\r
+ "<input type=\"submit\" value=\"Read!\">"\r
+ "</form>",\r
+ user->name,\r
+ user->name);\r
+ for (int i = 0; i < arrlen(posts); i++) {\r
+ html_post(posts[i], html+strlen(html), HTML_LEN-strlen(html));\r
+ }\r
+ snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
+ "</body>"\r
+ "</html>");\r
+ arrfree(posts);\r
+ return html;\r
+}\r
+\r
+char *\r
+html_user(User * user)\r
+{\r
+ static char html[HTML_LEN];\r
+ snprintf(html, HTML_LEN,\r
+ "<!DOCTYPE html>"\r
+ "<html>"\r
+ "<body>"\r
+ "<form action=\"/api/post/%s\" method=\"post\">"\r
+ "<input type=\"text\" name=\"content\" autofocus><br>"\r
+ "<input type=\"submit\" value=\"Chirp!\">"\r
+ "</form>",\r
+ user->name);\r
+ snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
+ "<form action=\"/api/write/%s\" method=\"post\">"\r
+ "<input type=\"submit\" value=\"Write!\">"\r
+ "</form>"\r
+ "<form action=\"/api/read/%s\" method=\"post\">"\r
+ "<input type=\"submit\" value=\"Read!\">"\r
+ "</form>",\r
+ user->name,\r
+ user->name);\r
+ for (int i = arrlen(user->posts) - 1; i >= 0; i--) {\r
+ html_post(&user->posts[i], html+strlen(html), HTML_LEN-strlen(html));\r
+ }\r
+ snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
+ "</body>"\r
+ "</html>");\r
+ return html;\r
+}\r
\r
-static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {\r
- if (ev == MG_EV_HTTP_MSG) {\r
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;\r
- if (mg_strcmp(hm->method, mg_str("post"))) {\r
- printf("POST: %.*s\n", hm->body.len, hm->body.ptr);\r
+\r
+\r
+static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data)\r
+{\r
+ if (ev == MG_EV_HTTP_MSG)\r
+ {\r
+ struct mg_http_message *hm = (struct mg_http_message *)ev_data;\r
+ \r
+ // POST\r
+ if (mg_strcmp(hm->method, mg_str("POST")) == 0)\r
+ {\r
+ // printf("POST: %.*s %.*s\n",\r
+ // hm->uri.len, hm->uri.ptr,\r
+ // hm->body.len, hm->body.ptr);\r
+ // printf("Message: %.*s\n",\r
+ // hm->message.len, hm->message.ptr);\r
+ \r
+\r
+ // TODO: make sure this diesnt error. shhhhhh!\r
+ struct mg_str caps[10];\r
+\r
+ if (mg_match(hm->uri, mg_str("/api/post/*"), caps)) {\r
+ User * user = UsersFind(caps[0]);\r
+ \r
+ if (user != NULL) {\r
+ Post newPost;\r
+ newPost.likes = 0;\r
+ newPost.comments = NULL;\r
+ newPost.timestamp = time(NULL);\r
+ newPost.user = user;\r
+ mg_http_get_var(&hm->body, "content", newPost.content, sizeof(newPost.content)-1);\r
+ arrput(user->posts, newPost);\r
+ }\r
+ }\r
+ else if (mg_match(hm->uri, mg_str("/api/comment/*/*"), caps)) {\r
+ User * user = UsersFind(caps[0]);\r
+ \r
+ if (user != NULL) {\r
+ Post * parent = (Post *)atoll(caps[1].ptr);\r
+ if (parent != NULL) {\r
+ Post newComment;\r
+ newComment.likes = 0;\r
+ newComment.comments = NULL;\r
+ newComment.timestamp = time(NULL);\r
+ newComment.user = user;\r
+ mg_http_get_var(&hm->body, "content", newComment.content, sizeof(newComment.content)-1);\r
+ arrput(parent->comments, newComment);\r
+ }\r
+ }\r
+ }\r
+ else if (mg_match(hm->uri, mg_str("/api/like/*/*"), caps)) {\r
+ User * user = UsersFind(caps[0]);\r
+ if (user != NULL) {\r
+ Post * post = (Post *)atoll(caps[1].ptr);\r
+ if (post != NULL)\r
+ post->likes++;\r
+ }\r
+ }\r
+ else if (mg_match(hm->uri, mg_str("/api/write/*"), caps)) {\r
+ User * user = UsersFind(caps[0]);\r
+ if (user != NULL) {\r
+ char filename[128];\r
+ snprintf(filename, 128, "data/users/%s", user->name);\r
+ FILE * f = fopen(filename, "w");\r
+ UserWrite(user, f);\r
+ fclose(f);\r
+ }\r
+ }\r
+ else if (mg_match(hm->uri, mg_str("/api/read/*"), caps)) {\r
+ User * user = UsersFind(caps[0]);\r
+ if (user != NULL) {\r
+ char filename[128];\r
+ snprintf(filename, 128, "data/users/%s", user->name);\r
+ FILE * f = fopen(filename, "r");\r
+ UserRead(user, f);\r
+ fclose(f);\r
+ }\r
+ }\r
+ http_redirect(c, hm);\r
}\r
- if (mg_http_match_uri(hm, "/user/*/post")) {\r
- struct mg_str caps[2];\r
- printf("uri: %.*s\n", hm->uri.len, hm->uri.ptr);\r
- mg_match(hm->uri, mg_str("/user/*/post"), caps);\r
- mg_http_reply(c, 200, "", html_user_post(caps[0]));\r
+ // GET\r
+ else if (mg_strcmp(hm->method, mg_str("GET")) == 0)\r
+ {\r
+ struct mg_str caps[2];\r
+\r
+ if (mg_match(hm->uri, mg_str("/home/*"), caps)) {\r
+ User * user = UsersFind(caps[0]);\r
+\r
+ if (user == NULL)\r
+ mg_http_reply(c, 200, "", html_user_not_found());\r
+ else\r
+ mg_http_reply(c, 200, "", html_home(user));\r
+ }\r
+ else if (mg_match(hm->uri, mg_str("/user/*"), caps)) {\r
+ User * user = UsersFind(caps[0]);\r
+\r
+ if (user == NULL)\r
+ mg_http_reply(c, 200, "", html_user_not_found());\r
+ else\r
+ mg_http_reply(c, 200, "", html_user(user));\r
+ }\r
+ else {\r
+ mg_http_reply(c, 404, "", html_404());\r
+ }\r
}\r
else {\r
- mg_http_reply(c, 404, "", "Not found :/");\r
+ mg_http_reply(c, 404, "", "Unknown");\r
}\r
}\r
}\r
\r
-int main(int argc, char *argv[]) {\r
+int main(int argc, char *argv[])\r
+{\r
+ UsersSetup();\r
+\r
struct mg_mgr mgr;\r
- mg_mgr_init(&mgr); // Init manager\r
- mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr); // Setup listener\r
- for (;;) mg_mgr_poll(&mgr, 1000); // Event loop\r
- mg_mgr_free(&mgr); // Cleanup\r
+ mg_mgr_init(&mgr);\r
+ mg_http_listen(&mgr, "http://[::]:8000", fn, &mgr);\r
+ while (1)\r
+ mg_mgr_poll(&mgr, 1000);\r
+ mg_mgr_free(&mgr);\r
return 0;\r
}\r