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.
7 #include <microhttpd.h>
8 #include <yajl/yajl_gen.h>
11 #include "subscription.h"
13 #include "canned_responses.h"
17 yajl_gen_config yajl_c = { 0, 0 };
20 struct MHD_PostProcessor *pp;
26 struct MHD_PostProcessor *pp;
32 struct subscribe_state {
33 struct MHD_PostProcessor *pp;
47 struct blergref_state {
50 struct blergref *results;
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;
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);
70 memcpy(buf, ybuf + gs->yoff, bytes_remaining);
72 yajl_gen_clear(gs->g);
73 return bytes_remaining;
80 if (pos == 0) { /* Start iterating */
81 yajl_gen_array_open(gs->g);
84 /* Snarf one record */
85 json_generate_one_record(gs->g, NULL, gs->b, gs->entries[gs->i], 0);
88 yajl_gen_array_close(gs->g);
94 yajl_gen_get_buf(gs->g, &ybuf, &len);
96 memcpy(buf, ybuf, max);
100 memcpy(buf, ybuf, len);
101 yajl_gen_clear(gs->g);
106 void GET_generate_list_free(void *cls) {
107 struct get_state *gs = cls;
110 yajl_gen_free(gs->g);
115 ssize_t GET_generate_blergref_list(void *cls, uint64_t pos, char *buf, size_t max) {
116 struct blergref_state *bs = cls;
118 const unsigned char *ybuf;
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);
129 memcpy(buf, ybuf + bs->yoff, bytes_remaining);
131 yajl_gen_clear(bs->g);
132 return bytes_remaining;
139 if (pos == 0) { /* Start iterating */
140 yajl_gen_array_open(bs->g);
143 /* Snarf one record */
144 b = blerg_open(bs->results[bs->i].author);
146 json_generate_one_record(bs->g, bs->results[bs->i].author, b, bs->results[bs->i].record, 0);
151 yajl_gen_array_close(bs->g);
157 yajl_gen_get_buf(bs->g, &ybuf, &len);
159 memcpy(buf, ybuf, max);
163 memcpy(buf, ybuf, len);
164 yajl_gen_clear(bs->g);
169 void GET_generate_blergref_list_free(void *cls) {
170 struct blergref_state *bs = cls;
172 yajl_gen_free(bs->g);
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;
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;
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;
196 if (strncmp(key, "data", 5) == 0) {
197 if (ps->data == NULL) {
198 ps->data_size = size;
199 ps->data = malloc(size);
201 if (ps->data_size + size > MAX_RECORD_SIZE) {
202 size = MAX_RECORD_SIZE - ps->data_size;
204 ps->data_size += size;
205 ps->data = realloc(ps->data, ps->data_size);
207 memcpy(ps->data + off, data, size);
208 if (ps->data_size == MAX_RECORD_SIZE)
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;
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;
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);
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));
240 uint64_t record_count = blerg_get_record_count(b);
242 if (from > to || from >= record_count || to >= record_count || to - from > 99) {
248 gs->entries = make_sequential_list(from, to);
251 gs->g = yajl_gen_alloc(&yajl_c, NULL);
252 gs->yoff = gs->done = 0;
254 response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
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;
264 bs->yoff = bs->done = 0;
266 return MHD_create_response_from_callback(-1, 262144, &GET_generate_blergref_list, bs, &GET_generate_blergref_list_free);
269 int check_auth(struct MHD_Connection *connection, const char *method, const char *upload_data, size_t *upload_data_size, void **ptr) {
270 struct auth_state *as = (struct auth_state *) *ptr;
272 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
273 return respond_405(connection);
275 as = malloc(sizeof(struct auth_state));
276 as->username[0] = as->password[0] = 0;
277 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
282 if (*upload_data_size) {
283 MHD_post_process(as->pp, upload_data, *upload_data_size);
284 *upload_data_size = 0;
288 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
289 if (!auth_check_token(as->username, given_token))
290 return respond_403(connection);
296 ahc_derp (void *cls, struct MHD_Connection *connection, const char *url, const char *method,
297 const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) {
298 struct MHD_Response *response;
300 struct url_info info;
303 if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
305 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
306 return respond_405(connection);
313 return respond_404(connection);
315 ret = parse_url_info(url + 5, &info);
316 if ((ret & URL_INFO_NAME) == 0)
317 return respond_404(connection);
319 if (!blerg_exists(info.name))
320 return respond_404(connection);
324 struct blerg *b = blerg_open(info.name);
326 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
327 response = create_response_for_range(b, info.record, info.record_to);
328 } else if (ret & URL_INFO_RECORD) {
329 ret = blerg_fetch(b, info.record, &data, &len);
333 return respond_404(connection);
334 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
336 uint64_t record_count, from, to;
337 record_count = blerg_get_record_count(b);
338 if (record_count == 0) {
340 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
342 to = record_count - 1;
343 from = (record_count > 50 ? to - 49 : 0);
344 response = create_response_for_range(b, from, to);
348 if (response == NULL) {
350 return respond_JSON_Failure(connection);
353 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
354 MHD_destroy_response(response);
356 } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
358 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
359 return respond_405(connection);
366 return respond_404(connection);
368 ret = parse_url_info(url + 5, &info);
369 if ((ret & URL_INFO_NAME) == 0)
370 return respond_404(connection);
372 if (info.name[0] == 'H')
374 if (!tag_exists(info.name))
375 return respond_404(connection);
378 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
381 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
383 response = create_blergref_response(taglist, recs);
386 if (response == NULL)
387 return respond_JSON_Failure(connection);
389 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
390 MHD_destroy_response(response);
393 } else if (strncmp(url, "/put", 4) == 0) {
394 struct put_state *ps = (struct put_state *) *ptr;
396 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
397 return respond_405(connection);
400 return respond_404(connection);
404 struct put_state *ps = malloc(sizeof(struct put_state));
407 ps->pp = MHD_create_post_processor(connection, 16384, &POST_put_iterator, ps);
413 if (*upload_data_size) {
414 MHD_post_process(ps->pp, upload_data, *upload_data_size);
415 *upload_data_size = 0;
419 if (ps->data == NULL || ps->data_size == 0 || ps->username[0] == 0)
420 return respond_JSON_Failure(connection);
422 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
423 if (!auth_check_token(ps->username, given_token))
424 return respond_JSON_Failure(connection);
426 struct blerg *b = blerg_open(ps->username);
428 return respond_JSON_Failure(connection);
429 ret = blerg_store(b, ps->data, ps->data_size);
432 return respond_JSON_Failure(connection);
434 MHD_destroy_post_processor(ps->pp);
439 return respond_JSON_Success(connection);
440 } else if (strncmp(url, "/info", 5) == 0) {
444 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
445 return respond_405(connection);
451 return respond_404(connection);
453 ret = parse_url_info(url + 6, &info);
454 if ((ret & URL_INFO_NAME) == 0)
455 return respond_404(connection);
457 if (!blerg_exists(info.name))
458 return respond_404(connection);
462 struct blerg *b = blerg_open(info.name);
463 uint64_t record_count = blerg_get_record_count(b);
467 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
468 yajl_gen_map_open(g);
469 yajl_gen_string(g, "record_count", 12);
470 snprintf(number, 21, "%llu", record_count);
471 yajl_gen_string(g, number, strlen(number));
472 yajl_gen_map_close(g);
474 const unsigned char *ybuf;
475 yajl_gen_get_buf(g, &ybuf, &len);
477 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
478 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
479 MHD_destroy_response(response);
484 } else if (strncmp(url, "/create", 8) == 0) {
485 struct auth_state *as = (struct auth_state *) *ptr;
488 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
489 return respond_405(connection);
491 struct auth_state *as = malloc(sizeof(struct auth_state));
492 as->username[0] = as->password[0] = 0;
493 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
498 if (*upload_data_size) {
499 MHD_post_process(as->pp, upload_data, *upload_data_size);
500 *upload_data_size = 0;
504 if (as->username[0] == 0 || as->password[0] == 0)
505 return respond_JSON_Failure(connection);
507 if (blerg_exists(as->username))
508 return respond_JSON_Failure(connection);
510 struct blerg *b = blerg_open(as->username);
512 auth_set_password(as->username, as->password);
514 MHD_destroy_post_processor(as->pp);
518 return respond_JSON_Success(connection);
519 } else if (strncmp(url, "/login", 7) == 0) {
520 struct auth_state *as = (struct auth_state *) *ptr;
523 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
524 return respond_405(connection);
526 struct auth_state *as = malloc(sizeof(struct auth_state));
527 as->username[0] = as->password[0] = 0;
528 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
533 if (*upload_data_size) {
534 MHD_post_process(as->pp, upload_data, *upload_data_size);
535 *upload_data_size = 0;
539 if (as->username[0] == 0 || as->password[0] == 0)
540 return respond_JSON_Failure(connection);
542 if (!auth_login(as->username, as->password))
543 return respond_JSON_Failure(connection);
545 response = MHD_create_response_from_data(strlen(JSON_SUCCESS), JSON_SUCCESS, MHD_NO, MHD_NO);
547 char *token = auth_get_token(as->username);
549 snprintf(data, 512, "auth=%s", token);
550 MHD_add_response_header(response, "Set-Cookie", data);
554 MHD_destroy_post_processor(as->pp);
558 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
559 MHD_destroy_response(response);
562 } else if (strncmp(url, "/logout", 8) == 0) {
563 struct auth_state *as = (struct auth_state *) *ptr;
566 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
567 return respond_405(connection);
569 struct auth_state *as = malloc(sizeof(struct auth_state));
570 as->username[0] = as->password[0] = 0;
571 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
576 if (*upload_data_size) {
577 MHD_post_process(as->pp, upload_data, *upload_data_size);
578 *upload_data_size = 0;
582 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
583 if (auth_check_token(as->username, given_token)) {
584 auth_logout(as->username);
585 return respond_JSON_Success(connection);
587 return respond_JSON_Failure(connection);
589 } else if (strncmp(url, "/subscribe", 11) == 0 || strncmp(url, "/unsubscribe", 13) == 0) {
590 struct subscribe_state *ss = (struct subscribe_state *) *ptr;
593 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
594 return respond_405(connection);
596 struct subscribe_state *ss = malloc(sizeof(struct subscribe_state));
597 ss->username[0] = ss->to[0] = 0;
598 ss->pp = MHD_create_post_processor(connection, 1024, &POST_subscribe_iterator, ss);
603 if (*upload_data_size) {
604 MHD_post_process(ss->pp, upload_data, *upload_data_size);
605 *upload_data_size = 0;
609 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
610 if (auth_check_token(ss->username, given_token)) {
612 subscription_remove(ss->username, ss->to);
614 subscription_add(ss->username, ss->to);
616 return respond_JSON_Success(connection);
618 return respond_JSON_Failure(connection);
620 } else if (strncmp(url, "/feed", 6) == 0) {
621 struct auth_state *as = (struct auth_state *) *ptr;
624 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
625 return respond_405(connection);
627 struct auth_state *as = malloc(sizeof(struct auth_state));
628 as->username[0] = as->password[0] = 0;
629 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
634 if (*upload_data_size) {
635 MHD_post_process(as->pp, upload_data, *upload_data_size);
636 *upload_data_size = 0;
640 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
641 if (auth_check_token(as->username, given_token)) {
643 struct blergref *feedlist = subscription_list(as->username, 0, &recs, -1);
646 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
648 response = create_blergref_response(feedlist, recs);
651 if (response == NULL)
652 return respond_JSON_Failure(connection);
654 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
655 MHD_destroy_response(response);
659 return respond_JSON_Failure(connection);
661 } else if (strncmp(url, "/feedinfo", 9) == 0) {
662 ret = check_auth(connection, method, upload_data, upload_data_size, ptr);
666 struct auth_state *as = (struct auth_state *) *ptr;
669 return respond_404(connection);
671 ret = parse_url_info(url + 10, &info);
672 if ((ret & URL_INFO_NAME) == 0)
673 return respond_404(connection);
675 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
676 yajl_gen_map_open(g);
677 yajl_gen_string(g, "subscribed", 10);
678 yajl_gen_bool(g, is_subscribed(as->username, info.name));
679 yajl_gen_map_close(g);
681 const unsigned char *ybuf;
682 yajl_gen_get_buf(g, &ybuf, &len);
684 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
685 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
686 MHD_destroy_response(response);
693 return respond_404(connection);
698 int main(int argc, char *argv[]) {
699 struct MHD_Daemon *daemon;
705 daemon = MHD_start_daemon(MHD_USE_DEBUG, HTTP_BLERG_PORT, NULL, NULL, &ahc_derp, NULL, MHD_OPTION_END);
706 if (daemon == NULL) {
707 fprintf(stderr, "Could not start web server\n");
712 FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es);
713 if (MHD_get_fdset(daemon, &rs, &ws, &es, &max) != MHD_YES) {
714 fprintf(stderr, "Fatal error getting fd sets\n");
717 select(max + 1, &rs, &ws, &es, NULL);
720 MHD_stop_daemon(daemon);