]> gitweb.ps.run Git - chirp/blob - src/main.c
dff0d17a83bd554836810ac2472af7005e3de70e
[chirp] / src / main.c
1 #include <mongoose.h>\r
2 #define STB_DS_IMPLEMENTATION\r
3 #include <stb_ds.h>\r
4 \r
5 \r
6 #define NAME_LEN 32\r
7 #define CONTENT_LEN 1024\r
8 #define HTML_LEN 1024*8\r
9 \r
10 typedef struct post Post;\r
11 typedef struct user User;\r
12 \r
13 User * UsersFind(struct mg_str name);\r
14 \r
15 // Post\r
16 \r
17 struct post {\r
18   time_t timestamp;\r
19   User * user;\r
20   char content[CONTENT_LEN];\r
21 };\r
22 \r
23 \r
24 \r
25 // User\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 User\r
34 UserNew(const char * name) {\r
35   User result;\r
36   strcpy(result.name, name);\r
37   result.posts = NULL;\r
38   result.following = NULL;\r
39   return result;\r
40 }\r
41 \r
42 void\r
43 UserWrite(User * user) {\r
44   char filename[128];\r
45   snprintf(filename, 128, "data/users/%s", user->name);\r
46   FILE * f = fopen(filename, "w");\r
47 \r
48   int numFollowing = arrlen(user->following);\r
49   int numPosts = arrlen(user->posts);\r
50   fwrite(&numFollowing, sizeof(int), 1, f);\r
51   fwrite(&numPosts, sizeof(int), 1, f);\r
52   for (int i = 0; i < numFollowing; i++) {\r
53     fwrite(user->following[i]->name, 1, sizeof(user->following[i]->name), f);\r
54   }\r
55   for (int i = 0; i < numPosts; i++) {\r
56     fwrite(&user->posts[i].timestamp, sizeof(user->posts[i].timestamp), 1, f);\r
57     fwrite(user->posts[i].user->name, 1, sizeof(user->posts[i].user->name), f);\r
58     fwrite(user->posts[i].content, 1, sizeof(user->posts[i].content), f);\r
59   }\r
60   fclose(f);\r
61 }\r
62 \r
63 void\r
64 UserRead(User * user) {\r
65   char filename[128];\r
66   snprintf(filename, 128, "data/users/%s", user->name);\r
67   FILE * f = fopen(filename, "r");\r
68 \r
69   int numFollowing;\r
70   int numPosts;\r
71   fread(&numFollowing, sizeof(int), 1, f);\r
72   fread(&numPosts, sizeof(int), 1, f);\r
73   for (int i = 0; i < numFollowing; i++) {\r
74     char name[NAME_LEN];\r
75     fread(name, 1, sizeof(name), f);\r
76     arrput(user->following, UsersFind(mg_str(name)));\r
77   }\r
78   for (int i = 0; i < numPosts; i++) {\r
79     char name[NAME_LEN];\r
80     Post post;\r
81     fread(&post.timestamp, sizeof(post.timestamp), 1, f);\r
82     fread(name, 1, sizeof(name), f);\r
83     post.user = UsersFind(mg_str(name));\r
84     fread(post.content, 1, sizeof(post.content), f);\r
85   }\r
86   fclose(f);\r
87 }\r
88 \r
89 \r
90 \r
91 // Users\r
92 \r
93 User ** g_users;\r
94 static User pat, yas, tof;\r
95 \r
96 void\r
97 UsersSetup() {\r
98   pat = UserNew("pat");\r
99   yas = UserNew("yas");\r
100   tof = UserNew("tof");\r
101 \r
102   // UserRead(&pat);\r
103   // UserRead(&yas);\r
104   // UserRead(&tof);\r
105 \r
106   arrput(pat.following, &yas);\r
107   arrput(yas.following, &pat);\r
108   arrput(tof.following, &yas);\r
109   arrput(tof.following, &pat);\r
110 \r
111   arrput(g_users, &pat);\r
112   arrput(g_users, &yas);\r
113   arrput(g_users, &tof);\r
114 }\r
115 \r
116 User *\r
117 UsersFind(struct mg_str name) {\r
118   for (int i = 0; i < arrlen(g_users); i++) {\r
119     User * user = g_users[i];\r
120     if (strncmp(user->name, name.ptr, name.len) == 0)\r
121       return user;\r
122   }\r
123   return NULL;\r
124 }\r
125 \r
126 \r
127 \r
128 // HTML\r
129 \r
130 void\r
131 http_redirect_home(struct mg_connection * c, struct mg_http_message * hm, User * user) {\r
132   struct mg_str *referer = mg_http_get_header(hm, "Referer");\r
133 \r
134   static char redirectHeaders[128];\r
135   snprintf(redirectHeaders, 128,\r
136     "Location: %.*s\r\n",\r
137       referer->len, referer->ptr);\r
138   mg_http_reply(c, 303, redirectHeaders, "");\r
139 }\r
140 \r
141 char *\r
142 html_404()\r
143 {\r
144   static char html[1024];\r
145   snprintf(html, 1024,\r
146           "<!DOCTYPE html>"\r
147           "<html>"\r
148           "<body>"\r
149           "<p><i>Eeeeeeh?</i> <b>The Website's Under Construction</b></p>"\r
150           "</body>"\r
151           "</html>");\r
152   return html;\r
153 }\r
154 \r
155 char *\r
156 html_user_not_found()\r
157 {\r
158   static char html[1024];\r
159   snprintf(html, 1024,\r
160           "<!DOCTYPE html>"\r
161           "<html>"\r
162           "<body>"\r
163           "<p>User was not found :/</p>"\r
164           "</body>"\r
165           "</html>");\r
166   return html;\r
167 }\r
168 \r
169 char *\r
170 html_post(Post * post) {\r
171   static char html[2048];\r
172   snprintf(html, 2048,\r
173     "<p>[%s] <a href=\"/user/%s\">%s</a>: %s</p>",\r
174       ctime(&post->timestamp),\r
175       post->user->name,\r
176       post->user->name,\r
177       post->content);\r
178   return html;\r
179 }\r
180 \r
181 char *\r
182 html_home(User * user)\r
183 {\r
184   Post ** posts = NULL;\r
185   for (int i = 0; i < arrlen(user->following); i++) {\r
186     User * userF = user->following[i];\r
187     for (int j = 0; j < arrlen(userF->posts); j++) {\r
188       Post * post = &userF->posts[j];\r
189       bool inserted = false;\r
190       for (int k = 0; k < arrlen(posts); k++) {\r
191         if (post->timestamp > posts[k]->timestamp) {\r
192           arrins(posts, k, post);\r
193           inserted = true;\r
194           break;\r
195         }\r
196       }\r
197       if (! inserted)\r
198         arrput(posts, post);\r
199     }\r
200   }\r
201 \r
202   static char html[HTML_LEN];\r
203   snprintf(html, HTML_LEN,\r
204           "<!DOCTYPE html>"\r
205           "<html>"\r
206           "<body>"\r
207           "<form action=\"/api/post/%s\" method=\"post\">"\r
208             "<input type=\"text\" name=\"content\"><br>"\r
209             "<input type=\"submit\" value=\"Chirp!\">"\r
210           "</form>",\r
211           user->name);\r
212   for (int i = 0; i < arrlen(posts); i++) {\r
213     snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
214             html_post(posts[i]));\r
215   }\r
216   snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
217           "</body>"\r
218           "</html>");\r
219   return html;\r
220 }\r
221 \r
222 char *\r
223 html_user(User * user)\r
224 {\r
225   static char html[HTML_LEN];\r
226   snprintf(html, HTML_LEN,\r
227           "<!DOCTYPE html>"\r
228           "<html>"\r
229           "<body>"\r
230           "<form action=\"/api/post/%s\" method=\"post\">"\r
231             "<input type=\"text\" name=\"content\"><br>"\r
232             "<input type=\"submit\" value=\"Chirp!\">"\r
233           "</form>",\r
234           user->name);\r
235   for (int i = arrlen(user->posts) - 1; i >= 0; i--) {\r
236     snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
237             html_post(&user->posts[i]));\r
238   }\r
239   snprintf(html+strlen(html), HTML_LEN-strlen(html),\r
240           "</body>"\r
241           "</html>");\r
242   return html;\r
243 }\r
244 \r
245 \r
246 \r
247 static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data)\r
248 {\r
249   if (ev == MG_EV_HTTP_MSG)\r
250   {\r
251     struct mg_http_message *hm = (struct mg_http_message *)ev_data;\r
252     \r
253     // POST\r
254     if (mg_strcmp(hm->method, mg_str("POST")) == 0)\r
255     {\r
256       // printf("POST: %.*s  %.*s\n",\r
257       //   hm->uri.len, hm->uri.ptr,\r
258       //   hm->body.len, hm->body.ptr);\r
259       // printf("Message: %.*s\n",\r
260       //   hm->message.len, hm->message.ptr);\r
261       \r
262 \r
263       struct mg_str caps[2];\r
264 \r
265       if (mg_match(hm->uri, mg_str("/api/post/*"), caps)) {\r
266         User * user = UsersFind(caps[0]);\r
267         \r
268         if (user != NULL) {\r
269           Post newPost;\r
270           newPost.timestamp = time(NULL);\r
271           newPost.user = user;\r
272           mg_http_get_var(&hm->body, "content", newPost.content, sizeof(newPost.content)-1);\r
273           arrput(user->posts, newPost);\r
274 \r
275           // UserWrite(user);\r
276 \r
277           http_redirect_home(c, hm, user);\r
278         }\r
279       }\r
280     }\r
281     // GET\r
282     else if (mg_strcmp(hm->method, mg_str("GET")) == 0)\r
283     {\r
284       struct mg_str caps[2];\r
285 \r
286       if (mg_match(hm->uri, mg_str("/home/*"), caps)) {\r
287         User * user = UsersFind(caps[0]);\r
288 \r
289         if (user == NULL)\r
290           mg_http_reply(c, 200, "", html_user_not_found());\r
291         else\r
292           mg_http_reply(c, 200, "", html_home(user));\r
293       }\r
294       else if (mg_match(hm->uri, mg_str("/user/*"), caps)) {\r
295         User * user = UsersFind(caps[0]);\r
296 \r
297         if (user == NULL)\r
298           mg_http_reply(c, 200, "", html_user_not_found());\r
299         else\r
300           mg_http_reply(c, 200, "", html_user(user));\r
301       }\r
302       else {\r
303         mg_http_reply(c, 404, "", html_404());\r
304       }\r
305     }\r
306     else {\r
307         mg_http_reply(c, 404, "", "Unknown");\r
308     }\r
309   }\r
310 }\r
311 \r
312 int main(int argc, char *argv[])\r
313 {\r
314   UsersSetup();\r
315 \r
316   struct mg_mgr mgr;\r
317   mg_mgr_init(&mgr);\r
318   mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr);\r
319   while (1)\r
320     mg_mgr_poll(&mgr, 1000);\r
321   mg_mgr_free(&mgr);\r
322   return 0;\r
323 }\r