Add subscription feed fetching, clean up a bit
[blerg.git] / http / http_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 <stdlib.h>
6 #include <string.h>
7 #include <microhttpd.h>
8 #include <yajl/yajl_gen.h>
9 #include "database.h"
10 #include "tags.h"
11 #include "subscription.h"
12 #include "auth.h"
13 #include "canned_responses.h"
14 #include "app.h"
15 #include "config.h"
16
17 yajl_gen_config yajl_c = { 0, 0 };
18
19 struct auth_state {
20         struct MHD_PostProcessor *pp;
21         char username[33];
22         char password[33];
23 };
24
25 struct put_state {
26         struct MHD_PostProcessor *pp;
27         char username[33];
28         char *data;
29         int data_size;
30 };
31
32 struct subscribe_state {
33         struct MHD_PostProcessor *pp;
34         char username[33];
35         char to[33];
36 };
37
38 struct get_state {
39         struct blerg *b;
40         yajl_gen g;
41         unsigned int yoff;
42         uint64_t *entries;
43         uint64_t i;
44         int done;
45 };
46
47 struct blergref_state {
48         yajl_gen g;
49         unsigned int yoff;
50         struct blergref *results;
51         uint64_t i;
52         int done;
53 };
54
55 ssize_t GET_generate_list(void *cls, uint64_t pos, char *buf, size_t max) {
56         struct get_state *gs = cls;
57         const unsigned char *ybuf;
58         char *data;
59         char number[21];
60         unsigned int len;
61
62         if (gs->yoff > 0) {
63                 yajl_gen_get_buf(gs->g, &ybuf, &len);
64                 size_t bytes_remaining = len - gs->yoff;
65                 if (bytes_remaining > max) {
66                         memcpy(buf, ybuf + gs->yoff, max);
67                         gs->yoff += max;
68                         return max;
69                 } else {
70                         memcpy(buf, ybuf + gs->yoff, bytes_remaining);
71                         gs->yoff = 0;
72                         yajl_gen_clear(gs->g);
73                         return bytes_remaining;
74                 }
75         }
76
77         if (gs->done)
78                 return -1;
79
80         if (pos == 0) { /* Start iterating */
81                 yajl_gen_array_open(gs->g);
82         }
83
84         /* Snarf one record */
85         json_generate_one_record(gs->g, NULL, gs->b, gs->entries[gs->i], 0);
86
87         if (gs->i == 0) {
88                 yajl_gen_array_close(gs->g);
89                 gs->done = 1;
90         }
91         gs->i--;
92
93
94         yajl_gen_get_buf(gs->g, &ybuf, &len);
95         if (len > max) {
96                 memcpy(buf, ybuf, max);
97                 gs->yoff = max;
98                 return max;
99         } else {
100                 memcpy(buf, ybuf, len);
101                 yajl_gen_clear(gs->g);
102                 return len;
103         }
104 }
105
106 void GET_generate_list_free(void *cls) {
107         struct get_state *gs = cls;
108
109         blerg_close(gs->b);
110         yajl_gen_free(gs->g);
111         free(gs->entries);
112         free(gs);
113 }
114
115 ssize_t GET_generate_blergref_list(void *cls, uint64_t pos, char *buf, size_t max) {
116         struct blergref_state *bs = cls;
117         struct blerg *b;
118         const unsigned char *ybuf;
119         unsigned int len;
120
121         if (bs->yoff > 0) {
122                 yajl_gen_get_buf(bs->g, &ybuf, &len);
123                 size_t bytes_remaining = len - bs->yoff;
124                 if (bytes_remaining > max) {
125                         memcpy(buf, ybuf + bs->yoff, max);
126                         bs->yoff += max;
127                         return max;
128                 } else {
129                         memcpy(buf, ybuf + bs->yoff, bytes_remaining);
130                         bs->yoff = 0;
131                         yajl_gen_clear(bs->g);
132                         return bytes_remaining;
133                 }
134         }
135
136         if (bs->done)
137                 return -1;
138
139         if (pos == 0) { /* Start iterating */
140                 yajl_gen_array_open(bs->g);
141         }
142
143         /* Snarf one record */
144         b = blerg_open(bs->results[bs->i].author);
145         if (b != NULL) {
146                 json_generate_one_record(bs->g, bs->results[bs->i].author, b, bs->results[bs->i].record, 0);
147                 blerg_close(b);
148         }
149
150         if (bs->i == 0) {
151                 yajl_gen_array_close(bs->g);
152                 bs->done = 1;
153         }
154
155         bs->i--;
156
157         yajl_gen_get_buf(bs->g, &ybuf, &len);
158         if (len > max) {
159                 memcpy(buf, ybuf, max);
160                 bs->yoff = max;
161                 return max;
162         } else {
163                 memcpy(buf, ybuf, len);
164                 yajl_gen_clear(bs->g);
165                 return len;
166         }
167 }
168
169 void GET_generate_blergref_list_free(void *cls) {
170         struct blergref_state *bs = cls;
171
172         yajl_gen_free(bs->g);
173         free(bs->results);
174         free(bs);
175 }
176
177 int POST_auth_iterator(void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type, const char *transfer_encoding, const char *data, uint64_t off, size_t size) {
178         struct auth_state *as = cls;
179
180         if (strncmp(key, "username", 9) == 0) {
181                 if (size > 32) size = 32;
182                 memcpy(as->username, data, size);
183                 as->username[size] = 0;
184         } else if (strncmp(key, "password", 9) == 0) {
185                 if (size > 32) size = 32;
186                 memcpy(as->password, data, size);
187                 as->password[size] = 0;
188         }
189
190         return MHD_YES;
191 }
192
193 int POST_put_iterator(void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type, const char *transfer_encoding, const char *data, uint64_t off, size_t size) {
194         struct put_state *ps = cls;
195
196         if (strncmp(key, "data", 5) == 0) {
197                 if (ps->data == NULL) {
198                         ps->data_size = size;
199                         ps->data = malloc(size);
200                 } else {
201                         if (ps->data_size + size > MAX_RECORD_SIZE) {
202                                 size = MAX_RECORD_SIZE - ps->data_size;
203                         }
204                         ps->data_size += size;
205                         ps->data = realloc(ps->data, ps->data_size);
206                 }
207                 memcpy(ps->data + off, data, size);
208                 if (ps->data_size == MAX_RECORD_SIZE)
209                         return MHD_NO;
210         } else if (strncmp(key, "username", 9) == 0) {
211                 if (size > 32) size = 32;
212                 memcpy(ps->username, data, size);
213                 ps->username[size] = 0;
214         }
215
216         return MHD_YES;
217 }
218
219 int POST_subscribe_iterator(void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type, const char *transfer_encoding, const char *data, uint64_t off, size_t size) {
220         struct subscribe_state *ss = cls;
221
222         if (strncmp(key, "username", 9) == 0) {
223                 if (size > 32) size = 32;
224                 memcpy(ss->username, data, size);
225                 ss->username[size] = 0;
226         } else if (strncmp(key, "to", 3) == 0) {
227                 if (size > 32) size = 32;
228                 memcpy(ss->to, data, size);
229                 ss->to[size] = 0;
230         }
231
232         return MHD_YES;
233 }
234
235 struct MHD_Response *create_response_for_range(struct blerg *b, uint64_t from, uint64_t to) {
236         struct MHD_Response *response;
237         struct get_state *gs = malloc(sizeof(struct get_state));
238         gs->b = b;
239
240         uint64_t record_count = blerg_get_record_count(b);
241
242         if (from > to || from >= record_count || to >= record_count || to - from > 99) {
243                 blerg_close(b);
244                 free(gs);
245                 return NULL;
246         }
247
248         gs->entries = make_sequential_list(from, to);
249         gs->i = to - from;
250
251         gs->g = yajl_gen_alloc(&yajl_c, NULL);
252         gs->yoff = gs->done = 0;
253
254         response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
255
256         return response;
257 }
258
259 struct MHD_Response *create_blergref_response(struct blergref *results, uint64_t len) {
260         struct blergref_state *bs = malloc(sizeof(struct blergref_state));
261         bs->g = yajl_gen_alloc(&yajl_c, NULL);
262         bs->results = results;
263         bs->i = len - 1;
264         bs->yoff = bs->done = 0;
265
266         return MHD_create_response_from_callback(-1, 262144, &GET_generate_blergref_list, bs, &GET_generate_blergref_list_free);
267 }
268
269 static int
270 ahc_derp (void *cls, struct MHD_Connection *connection, const char *url, const char *method,
271           const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) {
272         struct MHD_Response *response;
273         int ret, len;
274         struct url_info info;
275         char *data;
276
277         if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
278                 if (*ptr == NULL) {
279                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
280                                 return respond_405(connection);
281
282                         *ptr = (void *) 1;
283                         return MHD_YES;
284                 }
285
286                 if (url[4] != '/')
287                         return respond_404(connection);
288
289                 ret = parse_url_info(url + 5, &info);
290                 if ((ret & URL_INFO_NAME) == 0)
291                         return respond_404(connection);
292
293                 if (!blerg_exists(info.name))
294                         return respond_404(connection);
295
296                 *ptr == NULL;
297
298                 struct blerg *b = blerg_open(info.name);
299
300                 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
301                         response = create_response_for_range(b, info.record, info.record_to);
302                 } else if (ret & URL_INFO_RECORD) {
303                         ret = blerg_fetch(b, info.record, &data, &len);
304                         blerg_close(b);
305
306                         if (ret == 0)
307                                 return respond_404(connection);
308                         response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
309                 } else {
310                         uint64_t record_count, from, to;
311                         record_count = blerg_get_record_count(b);
312                         if (record_count == 0) {
313                                 blerg_close(b);
314                                 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
315                         } else {
316                                 to = record_count - 1;
317                                 from = (record_count > 50 ? to - 49 : 0);
318                                 response = create_response_for_range(b, from, to);
319                         }
320                 }
321
322                 if (response == NULL) {
323                         blerg_close(b);
324                         return respond_JSON_Failure(connection);
325                 }
326
327                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
328                 MHD_destroy_response(response);
329                 return ret;
330         } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
331                 if (*ptr == NULL) {
332                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
333                                 return respond_405(connection);
334
335                         *ptr = (void *) 1;
336                         return MHD_YES;
337                 }
338
339                 if (url[4] != '/')
340                         return respond_404(connection);
341
342                 ret = parse_url_info(url + 5, &info);
343                 if ((ret & URL_INFO_NAME) == 0)
344                         return respond_404(connection);
345
346                 if (info.name[0] == 'H')
347                         info.name[0] = '#';
348                 if (!tag_exists(info.name))
349                         return respond_404(connection);
350
351                 int recs = 50;
352                 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
353
354                 if (recs == 0) {
355                         response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
356                 } else {
357                         response = create_blergref_response(taglist, recs);
358                 }
359
360                 if (response == NULL)
361                         return respond_JSON_Failure(connection);
362
363                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
364                 MHD_destroy_response(response);
365
366                 return ret;
367         } else if (strncmp(url, "/put", 4) == 0) {
368                 struct put_state *ps = (struct put_state *) *ptr;
369                 if (*ptr == NULL) {
370                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
371                                 return respond_405(connection);
372
373                         if (url[4] == '/')
374                                 return respond_404(connection);
375
376                         *ptr = (void *) 1;
377
378                         struct put_state *ps = malloc(sizeof(struct put_state));
379                         ps->data = NULL;
380                         ps->data_size = 0;
381                         ps->pp = MHD_create_post_processor(connection, 16384, &POST_put_iterator, ps);
382                         ps->username[0] = 0;
383                         *ptr = ps;
384                         return MHD_YES;
385                 }
386
387                 if (*upload_data_size) {
388                         MHD_post_process(ps->pp, upload_data, *upload_data_size);
389                         *upload_data_size = 0;
390                         return MHD_YES;
391                 }
392
393                 if (ps->data == NULL || ps->data_size == 0 || ps->username[0] == 0)
394                         return respond_JSON_Failure(connection);
395
396                 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
397                 if (!auth_check_token(ps->username, given_token))
398                         return respond_JSON_Failure(connection);
399
400                 struct blerg *b = blerg_open(ps->username);
401                 if (b == NULL)
402                         return respond_JSON_Failure(connection);
403                 ret = blerg_store(b, ps->data, ps->data_size);
404                 blerg_close(b);
405                 if (ret == -1)
406                         return respond_JSON_Failure(connection);
407
408                 MHD_destroy_post_processor(ps->pp);
409                 free(ps->data);
410                 free(ps);
411                 *ptr = NULL;
412
413                 return respond_JSON_Success(connection);
414         } else if (strncmp(url, "/info", 5) == 0) {
415                 if (*ptr == NULL) {
416                         *ptr = (void *) 1;
417
418                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
419                                 return respond_405(connection);
420                         return MHD_YES;
421                 }
422
423
424                 if (url[5] != '/')
425                         return respond_404(connection);
426
427                 ret = parse_url_info(url + 6, &info);
428                 if ((ret & URL_INFO_NAME) == 0)
429                         return respond_404(connection);
430
431                 if (!blerg_exists(info.name))
432                         return respond_404(connection);
433
434                 *ptr == NULL;
435
436                 struct blerg *b = blerg_open(info.name);
437                 uint64_t record_count = blerg_get_record_count(b);
438                 blerg_close(b);
439
440                 char number[21];
441                 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
442                 yajl_gen_map_open(g);
443                 yajl_gen_string(g, "record_count", 12);
444                 snprintf(number, 21, "%llu", record_count);
445                 yajl_gen_string(g, number, strlen(number));
446                 yajl_gen_map_close(g);
447
448                 const unsigned char *ybuf;
449                 yajl_gen_get_buf(g, &ybuf, &len);
450
451                 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
452                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
453                 MHD_destroy_response(response);
454
455                 yajl_gen_free(g);
456
457                 return ret;
458         } else if (strncmp(url, "/create", 8) == 0) {
459                 struct auth_state *as = (struct auth_state *) *ptr;
460
461                 if (as == NULL) {
462                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
463                                 return respond_405(connection);
464
465                         struct auth_state *as = malloc(sizeof(struct auth_state));
466                         as->username[0] = as->password[0] = 0;
467                         as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
468                         *ptr = as;
469                         return MHD_YES;
470                 }
471
472                 if (*upload_data_size) {
473                         MHD_post_process(as->pp, upload_data, *upload_data_size);
474                         *upload_data_size = 0;
475                         return MHD_YES;
476                 }
477
478                 if (as->username[0] == 0 || as->password[0] == 0)
479                         return respond_JSON_Failure(connection);
480
481                 if (blerg_exists(as->username))
482                         return respond_JSON_Failure(connection);
483
484                 struct blerg *b = blerg_open(as->username);
485                 blerg_close(b);
486                 auth_set_password(as->username, as->password);
487
488                 MHD_destroy_post_processor(as->pp);
489                 free(as);
490                 *ptr = NULL;
491
492                 return respond_JSON_Success(connection);
493         } else if (strncmp(url, "/login", 7) == 0) {
494                 struct auth_state *as = (struct auth_state *) *ptr;
495
496                 if (as == NULL) {
497                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
498                                 return respond_405(connection);
499
500                         struct auth_state *as = malloc(sizeof(struct auth_state));
501                         as->username[0] = as->password[0] = 0;
502                         as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
503                         *ptr = as;
504                         return MHD_YES;
505                 }
506
507                 if (*upload_data_size) {
508                         MHD_post_process(as->pp, upload_data, *upload_data_size);
509                         *upload_data_size = 0;
510                         return MHD_YES;
511                 }
512
513                 if (as->username[0] == 0 || as->password[0] == 0)
514                         return respond_JSON_Failure(connection);
515
516                 if (!auth_login(as->username, as->password))
517                         return respond_JSON_Failure(connection);
518
519                 response = MHD_create_response_from_data(strlen(JSON_SUCCESS), JSON_SUCCESS, MHD_NO, MHD_NO);
520
521                 char *token = auth_get_token(as->username);
522                 data = malloc(512);
523                 snprintf(data, 512, "auth=%s", token);
524                 MHD_add_response_header(response, "Set-Cookie", data);
525                 free(token);
526                 free(data);
527
528                 MHD_destroy_post_processor(as->pp);
529                 free(as);
530                 *ptr = NULL;
531
532                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
533                 MHD_destroy_response(response);
534
535                 return ret;
536         } else if (strncmp(url, "/logout", 8) == 0) {
537                 struct auth_state *as = (struct auth_state *) *ptr;
538
539                 if (as == NULL) {
540                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
541                                 return respond_405(connection);
542
543                         struct auth_state *as = malloc(sizeof(struct auth_state));
544                         as->username[0] = as->password[0] = 0;
545                         as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
546                         *ptr = as;
547                         return MHD_YES;
548                 }
549
550                 if (*upload_data_size) {
551                         MHD_post_process(as->pp, upload_data, *upload_data_size);
552                         *upload_data_size = 0;
553                         return MHD_YES;
554                 }
555
556                 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
557                 if (auth_check_token(as->username, given_token)) {
558                         auth_logout(as->username);
559                         return respond_JSON_Success(connection);
560                 } else {
561                         return respond_JSON_Failure(connection);
562                 }
563         } else if (strncmp(url, "/subscribe", 11) == 0 || strncmp(url, "/unsubscribe", 13) == 0) {
564                 struct subscribe_state *ss = (struct subscribe_state *) *ptr;
565
566                 if (ss == NULL) {
567                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
568                                 return respond_405(connection);
569
570                         struct subscribe_state *ss = malloc(sizeof(struct subscribe_state));
571                         ss->username[0] = ss->to[0] = 0;
572                         ss->pp = MHD_create_post_processor(connection, 1024, &POST_subscribe_iterator, ss);
573                         *ptr = ss;
574                         return MHD_YES;
575                 }
576
577                 if (*upload_data_size) {
578                         MHD_post_process(ss->pp, upload_data, *upload_data_size);
579                         *upload_data_size = 0;
580                         return MHD_YES;
581                 }
582
583                 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
584                 if (auth_check_token(ss->username, given_token)) {
585                         if (url[1] == 'u') {
586                                 subscription_remove(ss->username, ss->to);
587                         } else {
588                                 subscription_add(ss->username, ss->to);
589                         }
590                         return respond_JSON_Success(connection);
591                 } else {
592                         return respond_JSON_Failure(connection);
593                 }
594         } else if (strncmp(url, "/feed", 6) == 0) {
595                 struct auth_state *as = (struct auth_state *) *ptr;
596
597                 if (as == NULL) {
598                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
599                                 return respond_405(connection);
600
601                         struct auth_state *as = malloc(sizeof(struct auth_state));
602                         as->username[0] = as->password[0] = 0;
603                         as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
604                         *ptr = as;
605                         return MHD_YES;
606                 }
607
608                 if (*upload_data_size) {
609                         MHD_post_process(as->pp, upload_data, *upload_data_size);
610                         *upload_data_size = 0;
611                         return MHD_YES;
612                 }
613
614                 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
615                 if (auth_check_token(as->username, given_token)) {
616                         int recs = 50;
617                         struct blergref *feedlist = subscription_list(as->username, 0, &recs, -1);
618
619                         if (recs == 0) {
620                                 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
621                         } else {
622                                 response = create_blergref_response(feedlist, recs);
623                         }
624
625                         if (response == NULL)
626                                 return respond_JSON_Failure(connection);
627
628                         ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
629                         MHD_destroy_response(response);
630
631                         return ret;
632                 } else {
633                         return respond_JSON_Failure(connection);
634                 }
635         } else {
636                 return respond_404(connection);
637         }
638 }
639
640
641 int main(int argc, char *argv[]) {
642         struct MHD_Daemon *daemon;
643         fd_set rs, ws, es;
644         int max;
645
646         init_responses();
647
648         daemon = MHD_start_daemon(MHD_USE_DEBUG, HTTP_BLERG_PORT, NULL, NULL, &ahc_derp, NULL, MHD_OPTION_END);
649         if (daemon == NULL) {
650                 fprintf(stderr, "Could not start web server\n");
651                 return 1;
652         }
653
654         while (1) {
655                 FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es);
656                 if (MHD_get_fdset(daemon, &rs, &ws, &es, &max) != MHD_YES) {
657                         fprintf(stderr, "Fatal error getting fd sets\n");
658                         break;
659                 }
660                 select(max + 1, &rs, &ws, &es, NULL);
661                 MHD_run(daemon);
662         }
663         MHD_stop_daemon(daemon);
664         return 0;
665 }