Add new account center and account recovery frontends
[blerg.git] / cgi / cgi_blerg.c
1 /* Blerg is (C) 2011 The Dominion of Awesome, and is distributed under a
2  * BSD-style license.  Please see the COPYING file for details.
3  */
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <cgi-util.h>
8 #include <yajl/yajl_gen.h>
9 #include "database.h"
10 #include "tags.h"
11 #include "auth.h"
12 #include "subscription.h"
13 #include "json.h"
14 #include "canned_responses.h"
15 #include "app.h"
16 #include "config.h"
17
18 yajl_gen_config yajl_c = { 0, 0 };
19
20 int check_auth(struct auth_cookie *ac) {
21         const char *given_cookie = cgi_getcookie("auth");
22
23         if (parse_auth_cookie(given_cookie, ac) != 1) {
24                 respond_403();
25                 return 0;
26         }
27
28         if (!auth_check_token(ac->name, ac->token)) {
29                 respond_403();
30                 return 0;
31         }
32         return 1;
33 }
34
35 void respond_yajl(yajl_gen g) {
36         const unsigned char *ybuf;
37         unsigned int content_len;
38
39         yajl_gen_get_buf(g, &ybuf, &content_len);
40
41         printf("Content-type: application/json\r\n");
42         printf("Content-length: %d\r\n\r\n", content_len);
43         fwrite(ybuf, content_len, 1, stdout);
44 }
45
46 void respond_for_range(struct blerg *b, uint64_t from, uint64_t to) {
47         const unsigned char *ybuf;
48         unsigned int len;
49         uint64_t i;
50         yajl_gen g;
51         uint64_t record_count = blerg_get_record_count(b);
52
53         printf("Content-type: application/json\r\n\r\n");
54
55         if (from > to || from >= record_count || to >= record_count || to - from > 99) {
56                 respond_JSON_Failure();
57                 return;
58         }
59
60         g = yajl_gen_alloc(&yajl_c, NULL);
61         yajl_gen_array_open(g);
62
63         for (i = to; i != from - 1; i--) {
64                 json_generate_one_record(g, NULL, b, i, 0);
65                 yajl_gen_get_buf(g, &ybuf, &len);
66                 fwrite(ybuf, len, 1, stdout);
67                 yajl_gen_clear(g);
68         }
69
70         yajl_gen_array_close(g);
71         yajl_gen_get_buf(g, &ybuf, &len);
72         fwrite(ybuf, len, 1, stdout);
73         yajl_gen_free(g);
74 }
75
76 void respond_blergref_list(struct blergref * results, int i) {
77         const unsigned char *ybuf;
78         unsigned int len;
79         struct blerg *b;
80         yajl_gen g;
81
82         i--;
83
84         printf("Content-type: application/json\r\n\r\n");
85         g = yajl_gen_alloc(&yajl_c, NULL);
86
87         yajl_gen_array_open(g);
88
89         while (i >= 0) {
90                 b = blerg_open(results[i].author);
91                 if (b != NULL) {
92                         json_generate_one_record(g, results[i].author, b, results[i].record, 0);
93                         blerg_close(b);
94                 }
95                 yajl_gen_get_buf(g, &ybuf, &len);
96                 fwrite(ybuf, len, 1, stdout);
97                 yajl_gen_clear(g);
98
99                 i--;
100         }
101
102         yajl_gen_array_close(g);
103         yajl_gen_get_buf(g, &ybuf, &len);
104         fwrite(ybuf, len, 1, stdout);
105         yajl_gen_free(g);
106 }
107
108 int main(int argc, char *argv[]) {
109         char *path;
110         char *request_method;
111         int ret, len;
112         struct url_info info;
113         struct auth_cookie ac;
114         char *data;
115
116         if (!blerg_init())
117                 exit(1);
118
119         if (cgi_init() != CGIERR_NONE)
120                 exit(0);
121
122         path = getenv("PATH_INFO");
123         if (path == NULL) {
124                 respond_404();
125                 exit(0);
126         }
127         request_method = getenv("REQUEST_METHOD");
128         if (request_method == NULL) {
129                 fprintf(stderr, "Request method is null!?\n");
130                 exit(0);
131         }
132
133         if (strncmp(path, "/get", 4) == 0 && strlen(path) > 4) {
134                 if (strncmp(request_method, "GET", 4) != 0) {
135                         respond_405();
136                         exit(0);
137                 }
138
139                 if (path[4] != '/') {
140                         respond_404();
141                         exit(0);
142                 }
143
144                 ret = parse_url_info(path + 5, &info);
145                 if ((ret & URL_INFO_NAME) == 0) {
146                         respond_404();
147                         exit(0);
148                 }
149
150                 if (!blerg_exists(info.name)) {
151                         respond_404();
152                         exit(0);
153                 }
154
155                 struct blerg *b = blerg_open(info.name);
156
157                 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
158                         respond_for_range(b, info.record, info.record_to);
159                 } else if (ret & URL_INFO_RECORD) {
160                         ret = blerg_fetch(b, info.record, &data, &len);
161
162                         if (ret == 0) {
163                                 respond_404();
164                                 exit(0);
165                         }
166                         respond_simple_data(data, len);
167                 } else {
168                         uint64_t record_count, from, to;
169                         record_count = blerg_get_record_count(b);
170                         if (record_count == 0) {
171                                 respond_simple_data("[]", 2);
172                         } else {
173                                 to = record_count - 1;
174                                 from = (record_count > 50 ? to - 49 : 0);
175                                 respond_for_range(b, from, to);
176                         }
177                 }
178
179                 blerg_close(b);
180         } else if (strncmp(path, "/tag", 4) == 0 && strlen(path) > 4) {
181                 if (strcmp(request_method, "GET") != 0) {
182                         respond_405();
183                         exit(0);
184                 }
185
186                 if (path[4] != '/') {
187                         respond_404();
188                         exit(0);
189                 }
190
191                 ret = parse_url_info(path + 5, &info);
192                 if ((ret & URL_INFO_NAME) == 0) {
193                         respond_404();
194                         exit(0);
195                 }
196
197                 if (info.name[0] == 'H')
198                         info.name[0] = '#';
199                 if (!tag_exists(info.name)) {
200                         respond_404();
201                         exit(0);
202                 }
203
204                 int recs = 50;
205                 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
206
207                 if (recs == 0) {
208                         respond_simple_data("[]", 2);
209                 } else {
210                         respond_blergref_list(taglist, recs);
211                 }
212         } else if (strncmp(path, "/put", 4) == 0) {
213                 if (strcmp(request_method, "POST") != 0) {
214                         respond_405();
215                         exit(0);
216                 }
217
218                 if (!check_auth(&ac))
219                         exit(0);
220
221                 if (path[4] == '/') {
222                         respond_404();
223                         exit(0);
224                 }
225
226                 const char *data = cgi_getentrystr("data");
227                 if (data == NULL || data[0] == 0) {
228                         respond_JSON_Failure();
229                         exit(0);
230                 }
231
232                 struct blerg *b = blerg_open(ac.name);
233                 if (b == NULL) {
234                         respond_JSON_Failure();
235                         exit(0);
236                 }
237                 ret = blerg_store(b, data, strlen(data));
238                 blerg_close(b);
239                 if (ret == -1) {
240                         respond_JSON_Failure();
241                         exit(0);
242                 }
243
244                 respond_JSON_Success();
245         } else if (strncmp(path, "/info", 5) == 0) {
246                 if (strcmp(request_method, "GET") != 0) {
247                         respond_405();
248                         exit(0);
249                 }
250
251                 if (path[5] != '/') {
252                         respond_404();
253                         exit(0);
254                 }
255
256                 ret = parse_url_info(path + 6, &info);
257                 if ((ret & URL_INFO_NAME) == 0) {
258                         respond_404();
259                         exit(0);
260                 }
261
262                 if (!blerg_exists(info.name)) {
263                         respond_404();
264                         exit(0);
265                 }
266
267                 struct blerg *b = blerg_open(info.name);
268                 uint64_t record_count = blerg_get_record_count(b);
269                 blerg_close(b);
270
271                 char number[21];
272                 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
273                 yajl_gen_map_open(g);
274                 yajl_gen_string(g, (unsigned char *)"record_count", 12);
275                 snprintf(number, 21, "%llu", record_count);
276                 yajl_gen_string(g, (unsigned char *)number, strlen(number));
277                 yajl_gen_map_close(g);
278
279                 respond_yajl(g);
280
281                 yajl_gen_free(g);
282         } else if (strncmp(path, "/create", 8) == 0) {
283                 if (strcmp(request_method, "POST") != 0) {
284                         respond_405();
285                         exit(0);
286                 }
287
288                 const char *username = cgi_getentrystr("username");
289                 const char *password = cgi_getentrystr("password");
290                 if (username == NULL || username[0] == 0 ||
291                     password == NULL || password[0] == 0) {
292                         respond_JSON_Failure();
293                         exit(0);
294                 }
295
296                 if (blerg_exists(username)) {
297                         respond_JSON_Failure();
298                         exit(0);
299                 }
300
301                 struct blerg *b = blerg_open(username);
302                 if (b != NULL) {
303                         blerg_close(b);
304                         auth_set_password(username, password);
305                         
306                         respond_JSON_Success();
307                 } else {
308                         respond_JSON_Failure();
309                 }
310         } else if (strncmp(path, "/login", 7) == 0) {
311                 if (strcmp(request_method, "POST") != 0) {
312                         respond_405();
313                         exit(0);
314                 }
315
316                 const char *username = cgi_getentrystr("username");
317                 const char *password = cgi_getentrystr("password");
318                 if (username == NULL || username[0] == 0 ||
319                     password == NULL || password[0] == 0) {
320                         respond_JSON_Failure();
321                         exit(0);
322                 }
323
324                 char *token = auth_login(username, password);
325                 if (token == NULL) {
326                         respond_JSON_Failure();
327                         exit(0);
328                 }
329
330                 printf("Set-Cookie: auth=%s/%s\r\n", username, token);
331                 free(token);
332
333                 respond_JSON_Success();
334         } else if (strncmp(path, "/logout", 8) == 0) {
335                 if (strcmp(request_method, "POST") != 0) {
336                         respond_405();
337                         exit(0);
338                 }
339
340                 if (!check_auth(&ac))
341                         exit(0);
342
343                 auth_logout(ac.name, ac.token);
344                 printf("Set-Cookie: auth=X; Expires=Thu, 01 Jan 1970 00:00:00 GMT\r\n");
345                 respond_JSON_Success();
346         } else if (strncmp(path, "/subscribe", 10) == 0) {
347                 if (!check_auth(&ac))
348                         exit(0);
349
350                 if (path[10] != '/') {
351                         respond_404();
352                         exit(0);
353                 }
354
355                 ret = parse_url_info(path + 11, &info);
356                 if ((ret & URL_INFO_NAME) == 0) {
357                         respond_404();
358                         exit(0);
359                 }
360
361                 const char *subscribed = cgi_getentrystr("subscribed");
362
363                 if (strncmp(subscribed, "true", 4) == 0) {
364                         subscription_add(ac.name, info.name);
365                 } else if (strncmp(subscribed, "false", 5) == 0) {
366                         subscription_remove(ac.name, info.name);
367                 } else {
368                         respond_JSON_Failure();
369                         exit(0);
370                 }
371                 respond_JSON_Success();
372         } else if (strncmp(path, "/feed", 6) == 0) {
373                 if (!check_auth(&ac))
374                         exit(0);
375
376                 int recs = 50;
377                 struct blergref *feedlist = subscription_list(ac.name, 0, &recs, -1);
378
379                 if (recs == 0) {
380                         respond_simple_data("[]", 2);
381                 } else {
382                         respond_blergref_list(feedlist, recs);
383                 }
384         } else if (strncmp(path, "/status", 7) == 0) {
385                 if (!check_auth(&ac))
386                         exit(0);
387
388                 if (strncmp(request_method, "POST", 4) == 0) {
389                         const char *clear = cgi_getentrystr("clear");
390
391                         if (clear != NULL) {
392                                 struct blerg *b = blerg_open(ac.name);
393                                 if (strncmp(clear, "feed", 4) == 0) {
394                                         blerg_set_subscription_mark(b);
395                                 } else if (strncmp(clear, "mentioned", 9) == 0) {
396                                         blerg_set_status(b, BLERGSTATUS_MENTIONED, 0);
397                                 }
398                                 blerg_close(b);
399                                 respond_JSON_Success();
400                         }
401                 } else if (strncmp(request_method, "GET", 3) == 0) {
402                         yajl_gen g;
403
404                         if (path[7] == 0) {  /* No username */
405                                 g = yajl_gen_alloc(&yajl_c, NULL);
406                                 yajl_gen_map_open(g);
407
408                                 struct blerg *b = blerg_open(ac.name);
409                                 uint64_t subscription_mark = blerg_get_subscription_mark(b);
410                                 int mentioned = blerg_get_status(b, BLERGSTATUS_MENTIONED);
411                                 blerg_close(b);
412
413                                 yajl_gen_string(g, (unsigned char *)"feed_new", 8);
414                                 yajl_gen_integer(g, subscription_count_items(ac.name) - subscription_mark);
415
416                                 yajl_gen_string(g, (unsigned char *)"mentioned", 9);
417                                 yajl_gen_bool(g, mentioned);
418
419                                 yajl_gen_map_close(g);
420                                 respond_yajl(g);
421                                 yajl_gen_free(g);
422                         } else {  /* with username */
423                                 g = yajl_gen_alloc(&yajl_c, NULL);
424                                 yajl_gen_map_open(g);
425
426                                 yajl_gen_string(g, (unsigned char *)"subscribed", 10);
427                                 ret = parse_url_info(path + 8, &info);
428                                 if ((ret & URL_INFO_NAME) == 1) {
429                                         yajl_gen_bool(g, is_subscribed(ac.name, info.name));
430                                 } else {
431                                         yajl_gen_bool(g, 0);
432                                 }
433
434                                 yajl_gen_map_close(g);
435                                 respond_yajl(g);
436                                 yajl_gen_free(g);
437                         }
438                 } else {
439                         respond_405();
440                         exit(0);
441                 }
442         } else if (strncmp(path, "/passwd", 7) == 0) {
443                 if (!check_auth(&ac))
444                         exit(0);
445
446                 const char *password = cgi_getentrystr("password");
447                 const char *new_password = cgi_getentrystr("new_password");
448                 if (password == NULL || new_password == NULL) {
449                         respond_JSON_Failure();
450                 } else {
451                         if (auth_check_password(ac.name, password)) {
452                                 auth_set_password(ac.name, new_password);
453                                 respond_JSON_Success();
454                         } else {
455                                 respond_JSON_Failure();
456                         }
457                 }
458         } else {
459                 respond_404();
460                 exit(0);
461         }
462
463         cgi_quit();
464
465         return 0;
466 }