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