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) {
301 gs->entries = make_sequential_list(from, to);
304 gs->g = yajl_gen_alloc(&yajl_c, NULL);
305 gs->yoff = gs->done = 0;
307 response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
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;
317 bs->yoff = bs->done = 0;
319 return MHD_create_response_from_callback(-1, 262144, &GET_generate_blergref_list, bs, &GET_generate_blergref_list_free);
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;
327 struct url_info info;
330 if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
332 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
333 return respond_405(connection);
340 return respond_404(connection);
342 ret = parse_url_info(url + 5, &info);
343 if ((ret & URL_INFO_NAME) == 0)
344 return respond_404(connection);
346 if (!blerg_exists(info.name))
347 return respond_404(connection);
351 struct blerg *b = blerg_open(info.name);
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);
360 return respond_404(connection);
361 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
363 uint64_t record_count, from, to;
364 record_count = blerg_get_record_count(b);
365 if (record_count == 0) {
367 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
369 to = record_count - 1;
370 from = (record_count > 50 ? to - 49 : 0);
371 response = create_response_for_range(b, from, to);
375 if (response == NULL) {
377 return respond_JSON_Failure(connection);
380 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
381 MHD_destroy_response(response);
383 } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
385 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
386 return respond_405(connection);
393 return respond_404(connection);
395 ret = parse_url_info(url + 5, &info);
396 if ((ret & URL_INFO_NAME) == 0)
397 return respond_404(connection);
399 if (info.name[0] == 'H')
401 if (!tag_exists(info.name))
402 return respond_404(connection);
405 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
408 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
410 response = create_blergref_response(taglist, recs);
413 if (response == NULL)
414 return respond_JSON_Failure(connection);
416 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
417 MHD_destroy_response(response);
420 } else if (strncmp(url, "/put", 4) == 0) {
422 return respond_404(connection);
424 ret = process_and_check_put(connection, method, upload_data, upload_data_size, ptr);
428 struct put_state *ps = (struct put_state *) *ptr;
430 if (ps->data == NULL || ps->data_size == 0)
431 return respond_JSON_Failure(connection);
433 struct blerg *b = blerg_open(ps->username);
435 return respond_JSON_Failure(connection);
436 ret = blerg_store(b, ps->data, ps->data_size);
439 return respond_JSON_Failure(connection);
441 MHD_destroy_post_processor(ps->pp);
446 return respond_JSON_Success(connection);
447 } else if (strncmp(url, "/info", 5) == 0) {
451 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
452 return respond_405(connection);
458 return respond_404(connection);
460 ret = parse_url_info(url + 6, &info);
461 if ((ret & URL_INFO_NAME) == 0)
462 return respond_404(connection);
464 if (!blerg_exists(info.name))
465 return respond_404(connection);
469 struct blerg *b = blerg_open(info.name);
470 uint64_t record_count = blerg_get_record_count(b);
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);
481 const unsigned char *ybuf;
482 yajl_gen_get_buf(g, &ybuf, &len);
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);
491 } else if (strncmp(url, "/create", 8) == 0) {
492 ret = process_auth(connection, method, upload_data, upload_data_size, ptr);
496 struct auth_state *as = (struct auth_state *) *ptr;
498 if (as->username[0] == 0 || as->password[0] == 0)
499 return respond_JSON_Failure(connection);
501 if (blerg_exists(as->username))
502 return respond_JSON_Failure(connection);
504 struct blerg *b = blerg_open(as->username);
506 auth_set_password(as->username, as->password);
508 MHD_destroy_post_processor(as->pp);
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);
518 struct auth_state *as = (struct auth_state *) *ptr;
520 if (as->username[0] == 0 || as->password[0] == 0)
521 return respond_JSON_Failure(connection);
523 char *token = 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);
530 snprintf(data, 512, "auth=%s", token);
531 MHD_add_response_header(response, "Set-Cookie", data);
535 MHD_destroy_post_processor(as->pp);
539 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
540 MHD_destroy_response(response);
543 } else if (strncmp(url, "/logout", 8) == 0) {
544 ret = process_and_check_auth(connection, method, upload_data, upload_data_size, ptr);
548 struct auth_state *as = (struct auth_state *) *ptr;
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);
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);