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