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_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) {
178 struct put_state *ps = cls;
180 if (strncmp(key, "data", 5) == 0) {
181 if (ps->data == NULL) {
182 ps->data_size = size;
183 ps->data = malloc(size);
185 if (ps->data_size + size > MAX_RECORD_SIZE) {
186 size = MAX_RECORD_SIZE - ps->data_size;
188 ps->data_size += size;
189 ps->data = realloc(ps->data, ps->data_size);
191 memcpy(ps->data + off, data, size);
192 if (ps->data_size == MAX_RECORD_SIZE)
194 } else if (strncmp(key, "username", 9) == 0) {
195 if (size > 32) size = 32;
196 memcpy(ps->username, data, size);
197 ps->username[size] = 0;
203 int process_put(struct MHD_Connection *connection, const char *method, const char *upload_data, size_t *upload_data_size, void **ptr) {
204 struct put_state *ps = (struct put_state *) *ptr;
207 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
208 return respond_405(connection);
212 struct put_state *ps = malloc(sizeof(struct put_state));
215 ps->pp = MHD_create_post_processor(connection, 16384, &POST_put_iterator, ps);
221 if (*upload_data_size) {
222 MHD_post_process(ps->pp, upload_data, *upload_data_size);
223 *upload_data_size = 0;
230 int process_and_check_put(struct MHD_Connection *connection, const char *method, const char *upload_data, size_t *upload_data_size, void **ptr) {
231 struct put_state *ps = (struct put_state *) *ptr;
233 if (process_put(connection, method, upload_data, upload_data_size, ptr) == MHD_YES)
236 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
237 if (!auth_check_token(ps->username, given_token))
238 return respond_403(connection);
243 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) {
244 struct subscribe_state *ss = cls;
246 if (strncmp(key, "username", 9) == 0) {
247 if (size > 32) size = 32;
248 memcpy(ss->username, data, size);
249 ss->username[size] = 0;
250 } else if (strncmp(key, "to", 3) == 0) {
251 if (size > 32) size = 32;
252 memcpy(ss->to, data, size);
259 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) {
260 struct auth_state *as = cls;
262 if (strncmp(key, "username", 9) == 0) {
263 if (size > 32) size = 32;
264 memcpy(as->username, data, size);
265 as->username[size] = 0;
266 } else if (strncmp(key, "password", 9) == 0) {
267 if (size > 32) size = 32;
268 memcpy(as->password, data, size);
269 as->password[size] = 0;
275 int process_auth(struct MHD_Connection *connection, const char *method, const char *upload_data, size_t *upload_data_size, void **ptr) {
276 struct auth_state *as = (struct auth_state *) *ptr;
279 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
280 return respond_405(connection);
282 as = malloc(sizeof(struct auth_state));
283 as->username[0] = as->password[0] = 0;
284 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
289 if (*upload_data_size) {
290 MHD_post_process(as->pp, upload_data, *upload_data_size);
291 *upload_data_size = 0;
298 int process_and_check_auth(struct MHD_Connection *connection, const char *method, const char *upload_data, size_t *upload_data_size, void **ptr) {
299 struct auth_state *as = (struct auth_state *) *ptr;
301 if (process_auth(connection, method, upload_data, upload_data_size, ptr) == MHD_YES)
304 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
305 if (!auth_check_token(as->username, given_token))
306 return respond_403(connection);
311 struct MHD_Response *create_response_for_range(struct blerg *b, uint64_t from, uint64_t to) {
312 struct MHD_Response *response;
313 struct get_state *gs = malloc(sizeof(struct get_state));
316 uint64_t record_count = blerg_get_record_count(b);
318 if (from > to || from >= record_count || to >= record_count || to - from > 99) {
324 gs->entries = make_sequential_list(from, to);
327 gs->g = yajl_gen_alloc(&yajl_c, NULL);
328 gs->yoff = gs->done = 0;
330 response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
335 struct MHD_Response *create_blergref_response(struct blergref *results, uint64_t len) {
336 struct blergref_state *bs = malloc(sizeof(struct blergref_state));
337 bs->g = yajl_gen_alloc(&yajl_c, NULL);
338 bs->results = results;
340 bs->yoff = bs->done = 0;
342 return MHD_create_response_from_callback(-1, 262144, &GET_generate_blergref_list, bs, &GET_generate_blergref_list_free);
346 ahc_derp (void *cls, struct MHD_Connection *connection, const char *url, const char *method,
347 const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) {
348 struct MHD_Response *response;
350 struct url_info info;
353 if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
355 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
356 return respond_405(connection);
363 return respond_404(connection);
365 ret = parse_url_info(url + 5, &info);
366 if ((ret & URL_INFO_NAME) == 0)
367 return respond_404(connection);
369 if (!blerg_exists(info.name))
370 return respond_404(connection);
374 struct blerg *b = blerg_open(info.name);
376 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
377 response = create_response_for_range(b, info.record, info.record_to);
378 } else if (ret & URL_INFO_RECORD) {
379 ret = blerg_fetch(b, info.record, &data, &len);
383 return respond_404(connection);
384 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
386 uint64_t record_count, from, to;
387 record_count = blerg_get_record_count(b);
388 if (record_count == 0) {
390 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
392 to = record_count - 1;
393 from = (record_count > 50 ? to - 49 : 0);
394 response = create_response_for_range(b, from, to);
398 if (response == NULL) {
400 return respond_JSON_Failure(connection);
403 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
404 MHD_destroy_response(response);
406 } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
408 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
409 return respond_405(connection);
416 return respond_404(connection);
418 ret = parse_url_info(url + 5, &info);
419 if ((ret & URL_INFO_NAME) == 0)
420 return respond_404(connection);
422 if (info.name[0] == 'H')
424 if (!tag_exists(info.name))
425 return respond_404(connection);
428 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
431 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
433 response = create_blergref_response(taglist, recs);
436 if (response == NULL)
437 return respond_JSON_Failure(connection);
439 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
440 MHD_destroy_response(response);
443 } else if (strncmp(url, "/put", 4) == 0) {
445 return respond_404(connection);
447 ret = process_and_check_put(connection, method, upload_data, upload_data_size, ptr);
451 struct put_state *ps = (struct put_state *) *ptr;
453 if (ps->data == NULL || ps->data_size == 0)
454 return respond_JSON_Failure(connection);
456 struct blerg *b = blerg_open(ps->username);
458 return respond_JSON_Failure(connection);
459 ret = blerg_store(b, ps->data, ps->data_size);
462 return respond_JSON_Failure(connection);
464 MHD_destroy_post_processor(ps->pp);
469 return respond_JSON_Success(connection);
470 } else if (strncmp(url, "/info", 5) == 0) {
474 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
475 return respond_405(connection);
481 return respond_404(connection);
483 ret = parse_url_info(url + 6, &info);
484 if ((ret & URL_INFO_NAME) == 0)
485 return respond_404(connection);
487 if (!blerg_exists(info.name))
488 return respond_404(connection);
492 struct blerg *b = blerg_open(info.name);
493 uint64_t record_count = blerg_get_record_count(b);
497 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
498 yajl_gen_map_open(g);
499 yajl_gen_string(g, "record_count", 12);
500 snprintf(number, 21, "%llu", record_count);
501 yajl_gen_string(g, number, strlen(number));
502 yajl_gen_map_close(g);
504 const unsigned char *ybuf;
505 yajl_gen_get_buf(g, &ybuf, &len);
507 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
508 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
509 MHD_destroy_response(response);
514 } else if (strncmp(url, "/create", 8) == 0) {
515 ret = process_auth(connection, method, upload_data, upload_data_size, ptr);
519 struct auth_state *as = (struct auth_state *) *ptr;
521 if (as->username[0] == 0 || as->password[0] == 0)
522 return respond_JSON_Failure(connection);
524 if (blerg_exists(as->username))
525 return respond_JSON_Failure(connection);
527 struct blerg *b = blerg_open(as->username);
529 auth_set_password(as->username, as->password);
531 MHD_destroy_post_processor(as->pp);
535 return respond_JSON_Success(connection);
536 } else if (strncmp(url, "/login", 7) == 0) {
537 ret = process_auth(connection, method, upload_data, upload_data_size, ptr);
541 struct auth_state *as = (struct auth_state *) *ptr;
543 if (as->username[0] == 0 || as->password[0] == 0)
544 return respond_JSON_Failure(connection);
546 if (!auth_login(as->username, as->password))
547 return respond_JSON_Failure(connection);
549 response = MHD_create_response_from_data(strlen(JSON_SUCCESS), JSON_SUCCESS, MHD_NO, MHD_NO);
551 char *token = auth_get_token(as->username);
553 snprintf(data, 512, "auth=%s", token);
554 MHD_add_response_header(response, "Set-Cookie", data);
558 MHD_destroy_post_processor(as->pp);
562 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
563 MHD_destroy_response(response);
566 } else if (strncmp(url, "/logout", 8) == 0) {
567 ret = process_and_check_auth(connection, method, upload_data, upload_data_size, ptr);
571 struct auth_state *as = (struct auth_state *) *ptr;
573 auth_logout(as->username);
574 return respond_JSON_Success(connection);
575 } else if (strncmp(url, "/subscribe", 11) == 0 || strncmp(url, "/unsubscribe", 13) == 0) {
576 struct subscribe_state *ss = (struct subscribe_state *) *ptr;
579 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
580 return respond_405(connection);
582 struct subscribe_state *ss = malloc(sizeof(struct subscribe_state));
583 ss->username[0] = ss->to[0] = 0;
584 ss->pp = MHD_create_post_processor(connection, 1024, &POST_subscribe_iterator, ss);
589 if (*upload_data_size) {
590 MHD_post_process(ss->pp, upload_data, *upload_data_size);
591 *upload_data_size = 0;
595 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
596 if (auth_check_token(ss->username, given_token)) {
598 subscription_remove(ss->username, ss->to);
600 subscription_add(ss->username, ss->to);
602 return respond_JSON_Success(connection);
604 return respond_JSON_Failure(connection);
606 } else if (strncmp(url, "/feed", 6) == 0) {
607 ret = process_and_check_auth(connection, method, upload_data, upload_data_size, ptr);
611 struct auth_state *as = (struct auth_state *) *ptr;
614 struct blergref *feedlist = subscription_list(as->username, 0, &recs, -1);
617 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
619 response = create_blergref_response(feedlist, recs);
622 if (response == NULL)
623 return respond_JSON_Failure(connection);
625 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
626 MHD_destroy_response(response);
629 } else if (strncmp(url, "/feedinfo", 9) == 0) {
630 ret = process_and_check_auth(connection, method, upload_data, upload_data_size, ptr);
634 struct auth_state *as = (struct auth_state *) *ptr;
637 return respond_404(connection);
639 ret = parse_url_info(url + 10, &info);
640 if ((ret & URL_INFO_NAME) == 0)
641 return respond_404(connection);
643 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
644 yajl_gen_map_open(g);
645 yajl_gen_string(g, "subscribed", 10);
646 yajl_gen_bool(g, is_subscribed(as->username, info.name));
647 yajl_gen_map_close(g);
649 const unsigned char *ybuf;
650 yajl_gen_get_buf(g, &ybuf, &len);
652 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
653 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
654 MHD_destroy_response(response);
661 return respond_404(connection);
666 int main(int argc, char *argv[]) {
667 struct MHD_Daemon *daemon;
673 daemon = MHD_start_daemon(MHD_USE_DEBUG, HTTP_BLERG_PORT, NULL, NULL, &ahc_derp, NULL, MHD_OPTION_END);
674 if (daemon == NULL) {
675 fprintf(stderr, "Could not start web server\n");
680 FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es);
681 if (MHD_get_fdset(daemon, &rs, &ws, &es, &max) != MHD_YES) {
682 fprintf(stderr, "Fatal error getting fd sets\n");
685 select(max + 1, &rs, &ws, &es, NULL);
688 MHD_stop_daemon(daemon);