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