2 #include <mongoose.h>
\r
3 #define STB_DS_IMPLEMENTATION
\r
8 #define CONTENT_LEN 1024
\r
9 #define HTML_LEN 1024*8
\r
11 typedef struct Post Post;
\r
12 typedef struct User User;
\r
14 User * UsersFind(struct mg_str name);
\r
21 char content[CONTENT_LEN];
\r
28 char name[NAME_LEN];
\r
36 memset(&result, 0, sizeof(result));
\r
41 PostWrite(Post * post, FILE * f) {
\r
42 fwrite(&post->timestamp, 8, 1, f);
\r
43 fwrite(post->user->name, 1, sizeof(post->user->name), f);
\r
44 fwrite(post->content, 1, sizeof(post->content), f);
\r
45 fwrite(&post->likes, 4, 1, f);
\r
46 uint32_t numComments = arrlen(post->comments);
\r
47 fwrite(&numComments, 4, 1, f);
\r
48 for (int i = 0; i < numComments; i++) {
\r
49 PostWrite(&post->comments[i], f);
\r
54 PostRead(Post * post, FILE * f) {
\r
55 fread(&post->timestamp, 8, 1, f);
\r
56 char name[NAME_LEN];
\r
57 fread(name, 1, sizeof(name), f);
\r
58 post->user = UsersFind(mg_str(name));
\r
59 fread(post->content, 1, sizeof(post->content), f);
\r
60 fread(&post->likes, 4, 1, f);
\r
61 uint32_t numComments;
\r
62 fread(&numComments, 4, 1, f);
\r
63 post->comments = NULL;
\r
64 for (int i = 0; i < numComments; i++) {
\r
65 Post newComment = PostNew();
\r
66 PostRead(&newComment, f);
\r
67 arrput(post->comments, newComment);
\r
72 UserNew(const char * name) {
\r
74 memset(&result, 0, sizeof(result));
\r
75 strcpy(result.name, name);
\r
80 UserWrite(User * user, FILE * f) {
\r
81 fwrite(user->name, 1, sizeof(user->name), f);
\r
83 uint32_t numFollowing = arrlen(user->following);
\r
84 uint32_t numPosts = arrlen(user->posts);
\r
85 fwrite(&numFollowing, 4, 1, f);
\r
86 fwrite(&numPosts, 4, 1, f);
\r
88 for (int i = 0; i < numFollowing; i++) {
\r
89 fwrite(user->following[i]->name, 1, sizeof(user->following[i]->name), f);
\r
92 for (int i = 0; i < numPosts; i++) {
\r
93 PostWrite(&user->posts[i], f);
\r
98 UserRead(User * user, FILE * f) {
\r
99 fread(user->name, 1, sizeof(user->name), f);
\r
101 uint32_t numFollowing;
\r
103 fread(&numFollowing, 1, 4, f);
\r
104 fread(&numPosts, 1, 4, f);
\r
106 user->following = NULL;
\r
107 for (int i = 0; i < numFollowing; i++) {
\r
108 char name[NAME_LEN];
\r
109 fread(name, 1, sizeof(name), f);
\r
110 arrput(user->following, UsersFind(mg_str(name)));
\r
113 user->posts = NULL;
\r
114 for (int i = 0; i < numPosts; i++) {
\r
115 Post post = PostNew();
\r
116 PostRead(&post, f);
\r
117 arrput(user->posts, post);
\r
126 static User pat, yas, tof;
\r
130 pat = UserNew("pat");
\r
131 yas = UserNew("yas");
\r
132 tof = UserNew("tof");
\r
134 arrput(pat.following, &yas);
\r
135 arrput(yas.following, &pat);
\r
136 arrput(tof.following, &yas);
\r
137 arrput(tof.following, &pat);
\r
139 arrput(g_users, &pat);
\r
140 arrput(g_users, &yas);
\r
141 arrput(g_users, &tof);
\r
145 UsersFind(struct mg_str name) {
\r
146 for (int i = 0; i < arrlen(g_users); i++) {
\r
147 User * user = g_users[i];
\r
148 if (strncmp(user->name, name.ptr, name.len) == 0)
\r
159 http_redirect(struct mg_connection * c, struct mg_http_message * hm) {
\r
160 struct mg_str *referer = mg_http_get_header(hm, "Referer");
\r
162 static char redirectHeaders[128];
\r
163 snprintf(redirectHeaders, 128,
\r
164 "Location: %.*s\r\n",
\r
165 referer->len, referer->ptr);
\r
166 mg_http_reply(c, 303, redirectHeaders, "");
\r
172 static char html[1024];
\r
173 snprintf(html, 1024,
\r
177 "<p><i>Eeeeeeh?</i> <b>The Website's Under Construction</b></p>"
\r
184 html_user_not_found()
\r
186 static char html[1024];
\r
187 snprintf(html, 1024,
\r
191 "<p>User was not found :/</p>"
\r
198 html_post(Post * post, char * buffer, int bufferLen) {
\r
202 printed = snprintf(buffer, bufferLen,
\r
203 "<div style=\"border: 2px solid black; margin: 2px; padding: 2px;\">[%s] <a href=\"/user/%s\">%s</a>: %s <br />",
\r
204 ctime(&post->timestamp),
\r
208 buffer += printed; bufferLen -= printed; result += printed;
\r
210 if (post->likes > 0) {
\r
211 printed = snprintf(buffer, bufferLen,
\r
212 "Likes: %d<br />", post->likes);
\r
213 buffer += printed; bufferLen -= printed; result += printed;
\r
216 printed = snprintf(buffer, bufferLen,
\r
217 "<form action=\"/api/like/%s/%llu\" method=\"post\">"
\r
218 "<input type=\"submit\" value=\"Like!\">"
\r
222 buffer += printed; bufferLen -= printed; result += printed;
\r
224 if (arrlen(post->comments) > 0) {
\r
225 printed = snprintf(buffer, bufferLen,
\r
226 "Comments:<br />");
\r
227 buffer += printed; bufferLen -= printed; result += printed;
\r
229 for (int i = 0; i < arrlen(post->comments); i++) {
\r
230 printed = html_post(&post->comments[i], buffer, bufferLen);
\r
231 buffer += printed; bufferLen -= printed; result += printed;
\r
235 printed = snprintf(buffer, bufferLen,
\r
236 "<form action=\"/api/comment/%s/%llu\" method=\"post\">"
\r
237 "<input type=\"text\" name=\"content\" autofocus>"
\r
238 "<input type=\"submit\" value=\"Comment!\">"
\r
243 buffer += printed; bufferLen -= printed; result += printed;
\r
249 html_home(User * user)
\r
251 Post ** posts = NULL;
\r
252 for (int i = 0; i < arrlen(user->following); i++) {
\r
253 User * userF = user->following[i];
\r
254 for (int j = 0; j < arrlen(userF->posts); j++) {
\r
255 Post * post = &userF->posts[j];
\r
256 bool inserted = false;
\r
257 for (int k = 0; k < arrlen(posts); k++) {
\r
258 if (post->timestamp > posts[k]->timestamp) {
\r
259 arrins(posts, k, post);
\r
265 arrput(posts, post);
\r
269 static char html[HTML_LEN];
\r
270 snprintf(html, HTML_LEN,
\r
274 "<form action=\"/api/post/%s\" method=\"post\">"
\r
275 "<input type=\"text\" name=\"content\" autofocus><br>"
\r
276 "<input type=\"submit\" value=\"Chirp!\">"
\r
279 snprintf(html+strlen(html), HTML_LEN-strlen(html),
\r
280 "<form action=\"/api/write/%s\" method=\"post\">"
\r
281 "<input type=\"submit\" value=\"Write!\">"
\r
283 "<form action=\"/api/read/%s\" method=\"post\">"
\r
284 "<input type=\"submit\" value=\"Read!\">"
\r
288 for (int i = 0; i < arrlen(posts); i++) {
\r
289 html_post(posts[i], html+strlen(html), HTML_LEN-strlen(html));
\r
291 snprintf(html+strlen(html), HTML_LEN-strlen(html),
\r
299 html_user(User * user)
\r
301 static char html[HTML_LEN];
\r
302 snprintf(html, HTML_LEN,
\r
306 "<form action=\"/api/post/%s\" method=\"post\">"
\r
307 "<input type=\"text\" name=\"content\" autofocus><br>"
\r
308 "<input type=\"submit\" value=\"Chirp!\">"
\r
311 snprintf(html+strlen(html), HTML_LEN-strlen(html),
\r
312 "<form action=\"/api/write/%s\" method=\"post\">"
\r
313 "<input type=\"submit\" value=\"Write!\">"
\r
315 "<form action=\"/api/read/%s\" method=\"post\">"
\r
316 "<input type=\"submit\" value=\"Read!\">"
\r
320 for (int i = arrlen(user->posts) - 1; i >= 0; i--) {
\r
321 html_post(&user->posts[i], html+strlen(html), HTML_LEN-strlen(html));
\r
323 snprintf(html+strlen(html), HTML_LEN-strlen(html),
\r
331 static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data)
\r
333 if (ev == MG_EV_HTTP_MSG)
\r
335 struct mg_http_message *hm = (struct mg_http_message *)ev_data;
\r
338 if (mg_strcmp(hm->method, mg_str("POST")) == 0)
\r
340 // printf("POST: %.*s %.*s\n",
\r
341 // hm->uri.len, hm->uri.ptr,
\r
342 // hm->body.len, hm->body.ptr);
\r
343 // printf("Message: %.*s\n",
\r
344 // hm->message.len, hm->message.ptr);
\r
347 // TODO: make sure this diesnt error. shhhhhh!
\r
348 struct mg_str caps[10];
\r
350 if (mg_match(hm->uri, mg_str("/api/post/*"), caps)) {
\r
351 User * user = UsersFind(caps[0]);
\r
353 if (user != NULL) {
\r
356 newPost.comments = NULL;
\r
357 newPost.timestamp = time(NULL);
\r
358 newPost.user = user;
\r
359 mg_http_get_var(&hm->body, "content", newPost.content, sizeof(newPost.content)-1);
\r
360 arrput(user->posts, newPost);
\r
363 else if (mg_match(hm->uri, mg_str("/api/comment/*/*"), caps)) {
\r
364 User * user = UsersFind(caps[0]);
\r
366 if (user != NULL) {
\r
367 Post * parent = (Post *)atoll(caps[1].ptr);
\r
368 if (parent != NULL) {
\r
370 newComment.likes = 0;
\r
371 newComment.comments = NULL;
\r
372 newComment.timestamp = time(NULL);
\r
373 newComment.user = user;
\r
374 mg_http_get_var(&hm->body, "content", newComment.content, sizeof(newComment.content)-1);
\r
375 arrput(parent->comments, newComment);
\r
379 else if (mg_match(hm->uri, mg_str("/api/like/*/*"), caps)) {
\r
380 User * user = UsersFind(caps[0]);
\r
381 if (user != NULL) {
\r
382 Post * post = (Post *)atoll(caps[1].ptr);
\r
387 else if (mg_match(hm->uri, mg_str("/api/write/*"), caps)) {
\r
388 User * user = UsersFind(caps[0]);
\r
389 if (user != NULL) {
\r
390 char filename[128];
\r
391 snprintf(filename, 128, "data/users/%s", user->name);
\r
392 FILE * f = fopen(filename, "w");
\r
393 UserWrite(user, f);
\r
397 else if (mg_match(hm->uri, mg_str("/api/read/*"), caps)) {
\r
398 User * user = UsersFind(caps[0]);
\r
399 if (user != NULL) {
\r
400 char filename[128];
\r
401 snprintf(filename, 128, "data/users/%s", user->name);
\r
402 FILE * f = fopen(filename, "r");
\r
407 http_redirect(c, hm);
\r
410 else if (mg_strcmp(hm->method, mg_str("GET")) == 0)
\r
412 struct mg_str caps[2];
\r
414 if (mg_match(hm->uri, mg_str("/home/*"), caps)) {
\r
415 User * user = UsersFind(caps[0]);
\r
418 mg_http_reply(c, 200, "", html_user_not_found());
\r
420 mg_http_reply(c, 200, "", html_home(user));
\r
422 else if (mg_match(hm->uri, mg_str("/user/*"), caps)) {
\r
423 User * user = UsersFind(caps[0]);
\r
426 mg_http_reply(c, 200, "", html_user_not_found());
\r
428 mg_http_reply(c, 200, "", html_user(user));
\r
431 mg_http_reply(c, 404, "", html_404());
\r
435 mg_http_reply(c, 404, "", "Unknown");
\r
440 int main(int argc, char *argv[])
\r
446 mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr);
\r
448 mg_mgr_poll(&mgr, 1000);
\r