Modify backend to use new auth cookie format
[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                 free(gs);
298                 return NULL;
299         }
300
301         gs->entries = make_sequential_list(from, to);
302         gs->i = to - from;
303
304         gs->g = yajl_gen_alloc(&yajl_c, NULL);
305         gs->yoff = gs->done = 0;
306
307         response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
308
309         return response;
310 }
311
312 struct MHD_Response *create_blergref_response(struct blergref *results, uint64_t len) {
313         struct blergref_state *bs = malloc(sizeof(struct blergref_state));
314         bs->g = yajl_gen_alloc(&yajl_c, NULL);
315         bs->results = results;
316         bs->i = len - 1;
317         bs->yoff = bs->done = 0;
318
319         return MHD_create_response_from_callback(-1, 262144, &GET_generate_blergref_list, bs, &GET_generate_blergref_list_free);
320 }
321
322 static int
323 ahc_derp (void *cls, struct MHD_Connection *connection, const char *url, const char *method,
324           const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) {
325         struct MHD_Response *response;
326         int ret, len;
327         struct url_info info;
328         char *data;
329
330         if (strncmp(url, "/get", 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 (!blerg_exists(info.name))
347                         return respond_404(connection);
348
349                 *ptr == NULL;
350
351                 struct blerg *b = blerg_open(info.name);
352
353                 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
354                         response = create_response_for_range(b, info.record, info.record_to);
355                 } else if (ret & URL_INFO_RECORD) {
356                         ret = blerg_fetch(b, info.record, &data, &len);
357                         blerg_close(b);
358
359                         if (ret == 0)
360                                 return respond_404(connection);
361                         response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
362                 } else {
363                         uint64_t record_count, from, to;
364                         record_count = blerg_get_record_count(b);
365                         if (record_count == 0) {
366                                 blerg_close(b);
367                                 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
368                         } else {
369                                 to = record_count - 1;
370                                 from = (record_count > 50 ? to - 49 : 0);
371                                 response = create_response_for_range(b, from, to);
372                         }
373                 }
374
375                 if (response == NULL) {
376                         blerg_close(b);
377                         return respond_JSON_Failure(connection);
378                 }
379
380                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
381                 MHD_destroy_response(response);
382                 return ret;
383         } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
384                 if (*ptr == NULL) {
385                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
386                                 return respond_405(connection);
387
388                         *ptr = (void *) 1;
389                         return MHD_YES;
390                 }
391
392                 if (url[4] != '/')
393                         return respond_404(connection);
394
395                 ret = parse_url_info(url + 5, &info);
396                 if ((ret & URL_INFO_NAME) == 0)
397                         return respond_404(connection);
398
399                 if (info.name[0] == 'H')
400                         info.name[0] = '#';
401                 if (!tag_exists(info.name))
402                         return respond_404(connection);
403
404                 int recs = 50;
405                 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
406
407                 if (recs == 0) {
408                         response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
409                 } else {
410                         response = create_blergref_response(taglist, recs);
411                 }
412
413                 if (response == NULL)
414                         return respond_JSON_Failure(connection);
415
416                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
417                 MHD_destroy_response(response);
418
419                 return ret;
420         } else if (strncmp(url, "/put", 4) == 0) {
421                 if (url[4] == '/')
422                         return respond_404(connection);
423
424                 ret = process_and_check_put(connection, method, upload_data, upload_data_size, ptr);
425                 if (ret == MHD_YES)
426                         return MHD_YES;
427
428                 struct put_state *ps = (struct put_state *) *ptr;
429
430                 if (ps->data == NULL || ps->data_size == 0)
431                         return respond_JSON_Failure(connection);
432
433                 struct blerg *b = blerg_open(ps->username);
434                 if (b == NULL)
435                         return respond_JSON_Failure(connection);
436                 ret = blerg_store(b, ps->data, ps->data_size);
437                 blerg_close(b);
438                 if (ret == -1)
439                         return respond_JSON_Failure(connection);
440
441                 MHD_destroy_post_processor(ps->pp);
442                 free(ps->data);
443                 free(ps);
444                 *ptr = NULL;
445
446                 return respond_JSON_Success(connection);
447         } else if (strncmp(url, "/info", 5) == 0) {
448                 if (*ptr == NULL) {
449                         *ptr = (void *) 1;
450
451                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
452                                 return respond_405(connection);
453                         return MHD_YES;
454                 }
455
456
457                 if (url[5] != '/')
458                         return respond_404(connection);
459
460                 ret = parse_url_info(url + 6, &info);
461                 if ((ret & URL_INFO_NAME) == 0)
462                         return respond_404(connection);
463
464                 if (!blerg_exists(info.name))
465                         return respond_404(connection);
466
467                 *ptr == NULL;
468
469                 struct blerg *b = blerg_open(info.name);
470                 uint64_t record_count = blerg_get_record_count(b);
471                 blerg_close(b);
472
473                 char number[21];
474                 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
475                 yajl_gen_map_open(g);
476                 yajl_gen_string(g, "record_count", 12);
477                 snprintf(number, 21, "%llu", record_count);
478                 yajl_gen_string(g, number, strlen(number));
479                 yajl_gen_map_close(g);
480
481                 const unsigned char *ybuf;
482                 yajl_gen_get_buf(g, &ybuf, &len);
483
484                 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
485                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
486                 MHD_destroy_response(response);
487
488                 yajl_gen_free(g);
489
490                 return ret;
491         } else if (strncmp(url, "/create", 8) == 0) {
492                 ret = process_auth(connection, method, upload_data, upload_data_size, ptr);
493                 if (ret == MHD_YES)
494                         return MHD_YES;
495
496                 struct auth_state *as = (struct auth_state *) *ptr;
497
498                 if (as->username[0] == 0 || as->password[0] == 0)
499                         return respond_JSON_Failure(connection);
500
501                 if (blerg_exists(as->username))
502                         return respond_JSON_Failure(connection);
503
504                 struct blerg *b = blerg_open(as->username);
505                 blerg_close(b);
506                 auth_set_password(as->username, as->password);
507
508                 MHD_destroy_post_processor(as->pp);
509                 free(as);
510                 *ptr = NULL;
511
512                 return respond_JSON_Success(connection);
513         } else if (strncmp(url, "/login", 7) == 0) {
514                 ret = process_auth(connection, method, upload_data, upload_data_size, ptr);
515                 if (ret == MHD_YES)
516                         return MHD_YES;
517
518                 struct auth_state *as = (struct auth_state *) *ptr;
519
520                 if (as->username[0] == 0 || as->password[0] == 0)
521                         return respond_JSON_Failure(connection);
522
523                 char *token = auth_login(as->username, as->password);
524                 if (token == NULL)
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                 data = malloc(512);
530                 snprintf(data, 512, "auth=%s", token);
531                 MHD_add_response_header(response, "Set-Cookie", data);
532                 free(token);
533                 free(data);
534
535                 MHD_destroy_post_processor(as->pp);
536                 free(as);
537                 *ptr = NULL;
538
539                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
540                 MHD_destroy_response(response);
541
542                 return ret;
543         } else if (strncmp(url, "/logout", 8) == 0) {
544                 ret = process_and_check_auth(connection, method, upload_data, upload_data_size, ptr);
545                 if (ret == MHD_YES)
546                         return MHD_YES;
547
548                 struct auth_state *as = (struct auth_state *) *ptr;
549
550                 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
551                 auth_logout(as->username, given_token);
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 }