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;
41 struct blergref_state {
44 struct blergref *results;
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;
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);
64 memcpy(buf, ybuf + gs->yoff, bytes_remaining);
66 yajl_gen_clear(gs->g);
67 return bytes_remaining;
74 if (pos == 0) { /* Start iterating */
75 yajl_gen_array_open(gs->g);
78 /* Snarf one record */
79 json_generate_one_record(gs->g, NULL, gs->b, gs->entries[gs->i], 0);
82 yajl_gen_array_close(gs->g);
88 yajl_gen_get_buf(gs->g, &ybuf, &len);
90 memcpy(buf, ybuf, max);
94 memcpy(buf, ybuf, len);
95 yajl_gen_clear(gs->g);
100 void GET_generate_list_free(void *cls) {
101 struct get_state *gs = cls;
104 yajl_gen_free(gs->g);
109 ssize_t GET_generate_blergref_list(void *cls, uint64_t pos, char *buf, size_t max) {
110 struct blergref_state *bs = cls;
112 const unsigned char *ybuf;
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);
123 memcpy(buf, ybuf + bs->yoff, bytes_remaining);
125 yajl_gen_clear(bs->g);
126 return bytes_remaining;
133 if (pos == 0) { /* Start iterating */
134 yajl_gen_array_open(bs->g);
137 /* Snarf one record */
138 b = blerg_open(bs->results[bs->i].author);
140 json_generate_one_record(bs->g, bs->results[bs->i].author, b, bs->results[bs->i].record, 0);
145 yajl_gen_array_close(bs->g);
151 yajl_gen_get_buf(bs->g, &ybuf, &len);
153 memcpy(buf, ybuf, max);
157 memcpy(buf, ybuf, len);
158 yajl_gen_clear(bs->g);
163 void GET_generate_blergref_list_free(void *cls) {
164 struct blergref_state *bs = cls;
166 yajl_gen_free(bs->g);
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;
174 if (strncmp(key, "data", 5) == 0) {
175 if (ps->data == NULL) {
176 ps->data_size = size;
177 ps->data = malloc(size);
179 if (ps->data_size + size > MAX_RECORD_SIZE) {
180 size = MAX_RECORD_SIZE - ps->data_size;
182 ps->data_size += size;
183 ps->data = realloc(ps->data, ps->data_size);
185 memcpy(ps->data + off, data, size);
186 if (ps->data_size == MAX_RECORD_SIZE)
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;
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;
201 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
202 return respond_405(connection);
206 struct put_state *ps = malloc(sizeof(struct put_state));
209 ps->pp = MHD_create_post_processor(connection, 16384, &POST_put_iterator, ps);
215 if (*upload_data_size) {
216 MHD_post_process(ps->pp, upload_data, *upload_data_size);
217 *upload_data_size = 0;
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;
227 if (process_put(connection, method, upload_data, upload_data_size, ptr) == MHD_YES)
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);
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;
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;
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;
257 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
258 return respond_405(connection);
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);
267 if (*upload_data_size) {
268 MHD_post_process(as->pp, upload_data, *upload_data_size);
269 *upload_data_size = 0;
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;
279 if (process_auth(connection, method, upload_data, upload_data_size, ptr) == MHD_YES)
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);
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));
294 uint64_t record_count = blerg_get_record_count(b);
296 if (from > to || from >= record_count || to >= record_count || to - from > 99) {
302 gs->entries = make_sequential_list(from, to);
305 gs->g = yajl_gen_alloc(&yajl_c, NULL);
306 gs->yoff = gs->done = 0;
308 response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
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;
318 bs->yoff = bs->done = 0;
320 return MHD_create_response_from_callback(-1, 262144, &GET_generate_blergref_list, bs, &GET_generate_blergref_list_free);
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;
328 struct url_info info;
331 if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
333 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
334 return respond_405(connection);
341 return respond_404(connection);
343 ret = parse_url_info(url + 5, &info);
344 if ((ret & URL_INFO_NAME) == 0)
345 return respond_404(connection);
347 if (!blerg_exists(info.name))
348 return respond_404(connection);
352 struct blerg *b = blerg_open(info.name);
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);
361 return respond_404(connection);
362 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
364 uint64_t record_count, from, to;
365 record_count = blerg_get_record_count(b);
366 if (record_count == 0) {
368 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
370 to = record_count - 1;
371 from = (record_count > 50 ? to - 49 : 0);
372 response = create_response_for_range(b, from, to);
376 if (response == NULL) {
378 return respond_JSON_Failure(connection);
381 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
382 MHD_destroy_response(response);
384 } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
386 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
387 return respond_405(connection);
394 return respond_404(connection);
396 ret = parse_url_info(url + 5, &info);
397 if ((ret & URL_INFO_NAME) == 0)
398 return respond_404(connection);
400 if (info.name[0] == 'H')
402 if (!tag_exists(info.name))
403 return respond_404(connection);
406 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
409 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
411 response = create_blergref_response(taglist, recs);
414 if (response == NULL)
415 return respond_JSON_Failure(connection);
417 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
418 MHD_destroy_response(response);
421 } else if (strncmp(url, "/put", 4) == 0) {
423 return respond_404(connection);
425 ret = process_and_check_put(connection, method, upload_data, upload_data_size, ptr);
429 struct put_state *ps = (struct put_state *) *ptr;
431 if (ps->data == NULL || ps->data_size == 0)
432 return respond_JSON_Failure(connection);
434 struct blerg *b = blerg_open(ps->username);
436 return respond_JSON_Failure(connection);
437 ret = blerg_store(b, ps->data, ps->data_size);
440 return respond_JSON_Failure(connection);
442 MHD_destroy_post_processor(ps->pp);
447 return respond_JSON_Success(connection);
448 } else if (strncmp(url, "/info", 5) == 0) {
452 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
453 return respond_405(connection);
459 return respond_404(connection);
461 ret = parse_url_info(url + 6, &info);
462 if ((ret & URL_INFO_NAME) == 0)
463 return respond_404(connection);
465 if (!blerg_exists(info.name))
466 return respond_404(connection);
470 struct blerg *b = blerg_open(info.name);
471 uint64_t record_count = blerg_get_record_count(b);
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);
482 const unsigned char *ybuf;
483 yajl_gen_get_buf(g, &ybuf, &len);
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);
492 } else if (strncmp(url, "/create", 8) == 0) {
493 ret = process_auth(connection, method, upload_data, upload_data_size, ptr);
497 struct auth_state *as = (struct auth_state *) *ptr;
499 if (as->username[0] == 0 || as->password[0] == 0)
500 return respond_JSON_Failure(connection);
502 if (blerg_exists(as->username))
503 return respond_JSON_Failure(connection);
505 struct blerg *b = blerg_open(as->username);
507 auth_set_password(as->username, as->password);
509 MHD_destroy_post_processor(as->pp);
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);
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 (!auth_login(as->username, as->password))
525 return respond_JSON_Failure(connection);
527 response = MHD_create_response_from_data(strlen(JSON_SUCCESS), JSON_SUCCESS, MHD_NO, MHD_NO);
529 char *token = auth_get_token(as->username);
531 snprintf(data, 512, "auth=%s", token);
532 MHD_add_response_header(response, "Set-Cookie", data);
536 MHD_destroy_post_processor(as->pp);
540 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
541 MHD_destroy_response(response);
544 } else if (strncmp(url, "/logout", 8) == 0) {
545 ret = process_and_check_auth(connection, method, upload_data, upload_data_size, ptr);
549 struct auth_state *as = (struct auth_state *) *ptr;
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);
558 struct auth_state *as = (struct auth_state *) *ptr;
562 return respond_404(connection);
564 ret = parse_url_info(url + 13, &info);
565 if ((ret & URL_INFO_NAME) == 0)
566 return respond_404(connection);
568 subscription_remove(as->username, info.name);
571 return respond_404(connection);
573 ret = parse_url_info(url + 11, &info);
574 if ((ret & URL_INFO_NAME) == 0)
575 return respond_404(connection);
577 subscription_add(as->username, info.name);
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);
585 struct auth_state *as = (struct auth_state *) *ptr;
588 struct blergref *feedlist = subscription_list(as->username, 0, &recs, -1);
591 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
593 response = create_blergref_response(feedlist, recs);
596 if (response == NULL)
597 return respond_JSON_Failure(connection);
599 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
600 MHD_destroy_response(response);
603 } else if (strncmp(url, "/feedinfo", 9) == 0) {
604 ret = process_and_check_auth(connection, method, upload_data, upload_data_size, ptr);
608 struct auth_state *as = (struct auth_state *) *ptr;
611 return respond_404(connection);
613 ret = parse_url_info(url + 10, &info);
614 if ((ret & URL_INFO_NAME) == 0)
615 return respond_404(connection);
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);
623 const unsigned char *ybuf;
624 yajl_gen_get_buf(g, &ybuf, &len);
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);
635 return respond_404(connection);
640 int main(int argc, char *argv[]) {
641 struct MHD_Daemon *daemon;
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");
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");
659 select(max + 1, &rs, &ws, &es, NULL);
662 MHD_stop_daemon(daemon);