]> gitweb.ps.run Git - chirp/blob - src/main.c
read/write nested subcomments
[chirp] / src / main.c
1 #include <stdint.h>\r
2 #include <mongoose.h>\r
3 #define STB_DS_IMPLEMENTATION\r
4 #include <stb_ds.h>\r
5 \r
6 \r
7 #define NAME_LEN 32\r
8 #define CONTENT_LEN 1024\r
9 #define HTML_LEN 1024*8\r
10 \r
11 typedef struct Post Post;\r
12 typedef struct User User;\r
13 \r
14 User * UsersFind(struct mg_str name);\r
15 \r
16 // Post / User\r
17 \r
18 struct Post {\r
19   time_t timestamp;\r
20   User * user;\r
21   char content[CONTENT_LEN];\r
22   uint32_t likes;\r
23   Post * comments;\r
24   //Post * parent;\r
25 };\r
26 \r
27 struct User {\r
28   char name[NAME_LEN];\r
29   Post * posts;\r
30   User ** following;\r
31 };\r
32 \r
33 Post\r
34 PostNew() {\r
35   Post result;\r
36   memset(&result, 0, sizeof(result));\r
37   return result;\r
38 }\r
39 \r
40 void\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
50   }\r
51 }\r
52 \r
53 void\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
68   }\r
69 }\r
70 \r
71 User\r
72 UserNew(const char * name) {\r
73   User result;\r
74   memset(&result, 0, sizeof(result));\r
75   strcpy(result.name, name);\r
76   return result;\r
77 }\r
78 \r
79 void\r
80 UserWrite(User * user, FILE * f) {\r
81   fwrite(user->name, 1, sizeof(user->name), f);\r
82 \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
87 \r
88   for (int i = 0; i < numFollowing; i++) {\r
89     fwrite(user->following[i]->name, 1, sizeof(user->following[i]->name), f);\r
90   }\r
91 \r
92   for (int i = 0; i < numPosts; i++) {\r
93     PostWrite(&user->posts[i], f);\r
94   }\r
95 }\r
96 \r
97 void\r
98 UserRead(User * user, FILE * f) {\r
99   fread(user->name, 1, sizeof(user->name), f);\r
100 \r
101   uint32_t numFollowing;\r
102   uint32_t numPosts;\r
103   fread(&numFollowing, 1, 4, f);\r
104   fread(&numPosts, 1, 4, f);\r
105 \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
111   }\r
112 \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
118   }\r
119 }\r
120 \r
121 \r
122 \r
123 // Users\r
124 \r
125 User ** g_users;\r
126 static User pat, yas, tof;\r
127 \r
128 void\r
129 UsersSetup() {\r
130   pat = UserNew("pat");\r
131   yas = UserNew("yas");\r
132   tof = UserNew("tof");\r
133 \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
138 \r
139   arrput(g_users, &pat);\r
140   arrput(g_users, &yas);\r
141   arrput(g_users, &tof);\r
142 }\r
143 \r
144 User *\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
149       return user;\r
150   }\r
151   return NULL;\r
152 }\r
153 \r
154 \r
155 \r
156 // HTML\r
157 \r
158 void\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
161 \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
167 }\r
168 \r
169 char *\r
170 html_404()\r
171 {\r
172   static char html[1024];\r
173   snprintf(html, 1024,\r
174           "<!DOCTYPE html>"\r
175           "<html>"\r
176           "<body>"\r
177           "<p><i>Eeeeeeh?</i> <b>The Website's Under Construction</b></p>"\r
178           "</body>"\r
179           "</html>");\r
180   return html;\r
181 }\r
182 \r
183 char *\r
184 html_user_not_found()\r
185 {\r
186   static char html[1024];\r
187   snprintf(html, 1024,\r
188           "<!DOCTYPE html>"\r
189           "<html>"\r
190           "<body>"\r
191           "<p>User was not found :/</p>"\r
192           "</body>"\r
193           "</html>");\r
194   return html;\r
195 }\r
196 \r
197 int\r
198 html_post(Post * post, char * buffer, int bufferLen) {\r
199   int result = 0;\r
200   int printed = 0;\r
201 \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
205       post->user->name,\r
206       post->user->name,\r
207       post->content);\r
208   buffer += printed; bufferLen -= printed; result += printed;\r
209 \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
214   }\r
215 \r
216   printed = snprintf(buffer, bufferLen,\r
217     "<form action=\"/api/like/%s/%llu\" method=\"post\">"\r
218       "<input type=\"submit\" value=\"Like!\">"\r
219     "</form>",\r
220       post->user->name,\r
221       (size_t)post);\r
222   buffer += printed; bufferLen -= printed; result += printed;\r
223   \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
228 \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
232     }\r
233   }\r
234 \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
239     "</form>"\r
240     "</div>",\r
241       post->user->name,\r
242       (size_t)post);\r
243   buffer += printed; bufferLen -= printed; result += printed;\r
244 \r
245   return result;\r
246 }\r
247 \r
248 char *\r
249 html_home(User * user)\r
250 {\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
260           inserted = true;\r
261           break;\r
262         }\r
263       }\r
264       if (! inserted)\r
265         arrput(posts, post);\r
266     }\r
267   }\r
268 \r
269   static char html[HTML_LEN];\r
270   snprintf(html, HTML_LEN,\r
271           "<!DOCTYPE html>"\r
272           "<html>"\r
273           "<body>"\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
277           "</form>",\r
278           user->name);\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
282           "</form>"\r
283           "<form action=\"/api/read/%s\" method=\"post\">"\r
284             "<input type=\"submit\" value=\"Read!\">"\r
285           "</form>",\r
286           user->name,\r
287           user->name);\r
288   for (int i = 0; i < arrlen(posts); i++) {\r
289     html_post(posts[i], html+strlen(html), HTML_LEN-strlen(html));\r
290   }\r
291   snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
292           "</body>"\r
293           "</html>");\r
294   arrfree(posts);\r
295   return html;\r
296 }\r
297 \r
298 char *\r
299 html_user(User * user)\r
300 {\r
301   static char html[HTML_LEN];\r
302   snprintf(html, HTML_LEN,\r
303           "<!DOCTYPE html>"\r
304           "<html>"\r
305           "<body>"\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
309           "</form>",\r
310           user->name);\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
314           "</form>"\r
315           "<form action=\"/api/read/%s\" method=\"post\">"\r
316             "<input type=\"submit\" value=\"Read!\">"\r
317           "</form>",\r
318           user->name,\r
319           user->name);\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
322   }\r
323   snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
324           "</body>"\r
325           "</html>");\r
326   return html;\r
327 }\r
328 \r
329 \r
330 \r
331 static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data)\r
332 {\r
333   if (ev == MG_EV_HTTP_MSG)\r
334   {\r
335     struct mg_http_message *hm = (struct mg_http_message *)ev_data;\r
336     \r
337     // POST\r
338     if (mg_strcmp(hm->method, mg_str("POST")) == 0)\r
339     {\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
345       \r
346 \r
347       // TODO: make sure this diesnt error. shhhhhh!\r
348       struct mg_str caps[10];\r
349 \r
350       if (mg_match(hm->uri, mg_str("/api/post/*"), caps)) {\r
351         User * user = UsersFind(caps[0]);\r
352         \r
353         if (user != NULL) {\r
354           Post newPost;\r
355           newPost.likes = 0;\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
361         }\r
362       }\r
363       else if (mg_match(hm->uri, mg_str("/api/comment/*/*"), caps)) {\r
364         User * user = UsersFind(caps[0]);\r
365         \r
366         if (user != NULL) {\r
367           Post * parent = (Post *)atoll(caps[1].ptr);\r
368           if (parent != NULL) {\r
369             Post newComment;\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
376           }\r
377         }\r
378       }\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
383           if (post != NULL)\r
384             post->likes++;\r
385         }\r
386       }\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
394           fclose(f);\r
395         }\r
396       }\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
403           UserRead(user, f);\r
404           fclose(f);\r
405         }\r
406       }\r
407       http_redirect(c, hm);\r
408     }\r
409     // GET\r
410     else if (mg_strcmp(hm->method, mg_str("GET")) == 0)\r
411     {\r
412       struct mg_str caps[2];\r
413 \r
414       if (mg_match(hm->uri, mg_str("/home/*"), caps)) {\r
415         User * user = UsersFind(caps[0]);\r
416 \r
417         if (user == NULL)\r
418           mg_http_reply(c, 200, "", html_user_not_found());\r
419         else\r
420           mg_http_reply(c, 200, "", html_home(user));\r
421       }\r
422       else if (mg_match(hm->uri, mg_str("/user/*"), caps)) {\r
423         User * user = UsersFind(caps[0]);\r
424 \r
425         if (user == NULL)\r
426           mg_http_reply(c, 200, "", html_user_not_found());\r
427         else\r
428           mg_http_reply(c, 200, "", html_user(user));\r
429       }\r
430       else {\r
431         mg_http_reply(c, 404, "", html_404());\r
432       }\r
433     }\r
434     else {\r
435         mg_http_reply(c, 404, "", "Unknown");\r
436     }\r
437   }\r
438 }\r
439 \r
440 int main(int argc, char *argv[])\r
441 {\r
442   UsersSetup();\r
443 \r
444   struct mg_mgr mgr;\r
445   mg_mgr_init(&mgr);\r
446   mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr);\r
447   while (1)\r
448     mg_mgr_poll(&mgr, 1000);\r
449   mg_mgr_free(&mgr);\r
450   return 0;\r
451 }\r