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);
270 ahc_derp (void *cls, struct MHD_Connection *connection, const char *url, const char *method,
271 const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) {
272 struct MHD_Response *response;
274 struct url_info info;
277 if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
279 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
280 return respond_405(connection);
287 return respond_404(connection);
289 ret = parse_url_info(url + 5, &info);
290 if ((ret & URL_INFO_NAME) == 0)
291 return respond_404(connection);
293 if (!blerg_exists(info.name))
294 return respond_404(connection);
298 struct blerg *b = blerg_open(info.name);
300 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
301 response = create_response_for_range(b, info.record, info.record_to);
302 } else if (ret & URL_INFO_RECORD) {
303 ret = blerg_fetch(b, info.record, &data, &len);
307 return respond_404(connection);
308 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
310 uint64_t record_count, from, to;
311 record_count = blerg_get_record_count(b);
312 if (record_count == 0) {
314 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
316 to = record_count - 1;
317 from = (record_count > 50 ? to - 49 : 0);
318 response = create_response_for_range(b, from, to);
322 if (response == NULL) {
324 return respond_JSON_Failure(connection);
327 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
328 MHD_destroy_response(response);
330 } else if (strncmp(url, "/tag", 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 (info.name[0] == 'H')
348 if (!tag_exists(info.name))
349 return respond_404(connection);
352 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
355 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
357 response = create_blergref_response(taglist, recs);
360 if (response == NULL)
361 return respond_JSON_Failure(connection);
363 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
364 MHD_destroy_response(response);
367 } else if (strncmp(url, "/put", 4) == 0) {
368 struct put_state *ps = (struct put_state *) *ptr;
370 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
371 return respond_405(connection);
374 return respond_404(connection);
378 struct put_state *ps = malloc(sizeof(struct put_state));
381 ps->pp = MHD_create_post_processor(connection, 16384, &POST_put_iterator, ps);
387 if (*upload_data_size) {
388 MHD_post_process(ps->pp, upload_data, *upload_data_size);
389 *upload_data_size = 0;
393 if (ps->data == NULL || ps->data_size == 0 || ps->username[0] == 0)
394 return respond_JSON_Failure(connection);
396 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
397 if (!auth_check_token(ps->username, given_token))
398 return respond_JSON_Failure(connection);
400 struct blerg *b = blerg_open(ps->username);
402 return respond_JSON_Failure(connection);
403 ret = blerg_store(b, ps->data, ps->data_size);
406 return respond_JSON_Failure(connection);
408 MHD_destroy_post_processor(ps->pp);
413 return respond_JSON_Success(connection);
414 } else if (strncmp(url, "/info", 5) == 0) {
418 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
419 return respond_405(connection);
425 return respond_404(connection);
427 ret = parse_url_info(url + 6, &info);
428 if ((ret & URL_INFO_NAME) == 0)
429 return respond_404(connection);
431 if (!blerg_exists(info.name))
432 return respond_404(connection);
436 struct blerg *b = blerg_open(info.name);
437 uint64_t record_count = blerg_get_record_count(b);
441 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
442 yajl_gen_map_open(g);
443 yajl_gen_string(g, "record_count", 12);
444 snprintf(number, 21, "%llu", record_count);
445 yajl_gen_string(g, number, strlen(number));
446 yajl_gen_map_close(g);
448 const unsigned char *ybuf;
449 yajl_gen_get_buf(g, &ybuf, &len);
451 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
452 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
453 MHD_destroy_response(response);
458 } else if (strncmp(url, "/create", 8) == 0) {
459 struct auth_state *as = (struct auth_state *) *ptr;
462 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
463 return respond_405(connection);
465 struct auth_state *as = malloc(sizeof(struct auth_state));
466 as->username[0] = as->password[0] = 0;
467 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
472 if (*upload_data_size) {
473 MHD_post_process(as->pp, upload_data, *upload_data_size);
474 *upload_data_size = 0;
478 if (as->username[0] == 0 || as->password[0] == 0)
479 return respond_JSON_Failure(connection);
481 if (blerg_exists(as->username))
482 return respond_JSON_Failure(connection);
484 struct blerg *b = blerg_open(as->username);
486 auth_set_password(as->username, as->password);
488 MHD_destroy_post_processor(as->pp);
492 return respond_JSON_Success(connection);
493 } else if (strncmp(url, "/login", 7) == 0) {
494 struct auth_state *as = (struct auth_state *) *ptr;
497 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
498 return respond_405(connection);
500 struct auth_state *as = malloc(sizeof(struct auth_state));
501 as->username[0] = as->password[0] = 0;
502 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
507 if (*upload_data_size) {
508 MHD_post_process(as->pp, upload_data, *upload_data_size);
509 *upload_data_size = 0;
513 if (as->username[0] == 0 || as->password[0] == 0)
514 return respond_JSON_Failure(connection);
516 if (!auth_login(as->username, as->password))
517 return respond_JSON_Failure(connection);
519 response = MHD_create_response_from_data(strlen(JSON_SUCCESS), JSON_SUCCESS, MHD_NO, MHD_NO);
521 char *token = auth_get_token(as->username);
523 snprintf(data, 512, "auth=%s", token);
524 MHD_add_response_header(response, "Set-Cookie", data);
528 MHD_destroy_post_processor(as->pp);
532 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
533 MHD_destroy_response(response);
536 } else if (strncmp(url, "/logout", 8) == 0) {
537 struct auth_state *as = (struct auth_state *) *ptr;
540 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
541 return respond_405(connection);
543 struct auth_state *as = malloc(sizeof(struct auth_state));
544 as->username[0] = as->password[0] = 0;
545 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
550 if (*upload_data_size) {
551 MHD_post_process(as->pp, upload_data, *upload_data_size);
552 *upload_data_size = 0;
556 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
557 if (auth_check_token(as->username, given_token)) {
558 auth_logout(as->username);
559 return respond_JSON_Success(connection);
561 return respond_JSON_Failure(connection);
563 } else if (strncmp(url, "/subscribe", 11) == 0 || strncmp(url, "/unsubscribe", 13) == 0) {
564 struct subscribe_state *ss = (struct subscribe_state *) *ptr;
567 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
568 return respond_405(connection);
570 struct subscribe_state *ss = malloc(sizeof(struct subscribe_state));
571 ss->username[0] = ss->to[0] = 0;
572 ss->pp = MHD_create_post_processor(connection, 1024, &POST_subscribe_iterator, ss);
577 if (*upload_data_size) {
578 MHD_post_process(ss->pp, upload_data, *upload_data_size);
579 *upload_data_size = 0;
583 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
584 if (auth_check_token(ss->username, given_token)) {
586 subscription_remove(ss->username, ss->to);
588 subscription_add(ss->username, ss->to);
590 return respond_JSON_Success(connection);
592 return respond_JSON_Failure(connection);
594 } else if (strncmp(url, "/feed", 6) == 0) {
595 struct auth_state *as = (struct auth_state *) *ptr;
598 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
599 return respond_405(connection);
601 struct auth_state *as = malloc(sizeof(struct auth_state));
602 as->username[0] = as->password[0] = 0;
603 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
608 if (*upload_data_size) {
609 MHD_post_process(as->pp, upload_data, *upload_data_size);
610 *upload_data_size = 0;
614 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
615 if (auth_check_token(as->username, given_token)) {
617 struct blergref *feedlist = subscription_list(as->username, 0, &recs, -1);
620 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
622 response = create_blergref_response(feedlist, recs);
625 if (response == NULL)
626 return respond_JSON_Failure(connection);
628 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
629 MHD_destroy_response(response);
633 return respond_JSON_Failure(connection);
636 return respond_404(connection);
641 int main(int argc, char *argv[]) {
642 struct MHD_Daemon *daemon;
648 daemon = MHD_start_daemon(MHD_USE_DEBUG, HTTP_BLERG_PORT, NULL, NULL, &ahc_derp, NULL, MHD_OPTION_END);
649 if (daemon == NULL) {
650 fprintf(stderr, "Could not start web server\n");
655 FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es);
656 if (MHD_get_fdset(daemon, &rs, &ws, &es, &max) != MHD_YES) {
657 fprintf(stderr, "Fatal error getting fd sets\n");
660 select(max + 1, &rs, &ws, &es, NULL);
663 MHD_stop_daemon(daemon);