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>
12 #include "canned_responses.h"
16 yajl_gen_config yajl_c = { 0, 0 };
19 struct MHD_PostProcessor *pp;
25 struct MHD_PostProcessor *pp;
31 struct subscribe_state {
32 struct MHD_PostProcessor *pp;
54 ssize_t GET_generate_list(void *cls, uint64_t pos, char *buf, size_t max) {
55 struct get_state *gs = cls;
56 const unsigned char *ybuf;
62 yajl_gen_get_buf(gs->g, &ybuf, &len);
63 size_t bytes_remaining = len - gs->yoff;
64 if (bytes_remaining > max) {
65 memcpy(buf, ybuf + gs->yoff, max);
69 memcpy(buf, ybuf + gs->yoff, bytes_remaining);
71 yajl_gen_clear(gs->g);
72 return bytes_remaining;
79 if (pos == 0) { /* Start iterating */
80 yajl_gen_array_open(gs->g);
83 /* Snarf one record */
84 json_generate_one_record(gs->g, NULL, gs->b, gs->entries[gs->i], 0);
87 yajl_gen_array_close(gs->g);
93 yajl_gen_get_buf(gs->g, &ybuf, &len);
95 memcpy(buf, ybuf, max);
99 memcpy(buf, ybuf, len);
100 yajl_gen_clear(gs->g);
105 void GET_generate_list_free(void *cls) {
106 struct get_state *gs = cls;
109 yajl_gen_free(gs->g);
114 ssize_t GET_generate_taglist(void *cls, uint64_t pos, char *buf, size_t max) {
115 struct tag_state *ts = cls;
117 const unsigned char *ybuf;
121 yajl_gen_get_buf(ts->g, &ybuf, &len);
122 size_t bytes_remaining = len - ts->yoff;
123 if (bytes_remaining > max) {
124 memcpy(buf, ybuf + ts->yoff, max);
128 memcpy(buf, ybuf + ts->yoff, bytes_remaining);
130 yajl_gen_clear(ts->g);
131 return bytes_remaining;
138 if (pos == 0) { /* Start iterating */
139 yajl_gen_array_open(ts->g);
142 /* Snarf one record */
143 b = blerg_open(ts->results[ts->i].author);
145 json_generate_one_record(ts->g, ts->results[ts->i].author, b, ts->results[ts->i].record, 0);
150 yajl_gen_array_close(ts->g);
156 yajl_gen_get_buf(ts->g, &ybuf, &len);
158 memcpy(buf, ybuf, max);
162 memcpy(buf, ybuf, len);
163 yajl_gen_clear(ts->g);
168 void GET_generate_taglist_free(void *cls) {
169 struct tag_state *ts = cls;
171 yajl_gen_free(ts->g);
176 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) {
177 struct auth_state *as = cls;
179 if (strncmp(key, "username", 9) == 0) {
180 if (size > 32) size = 32;
181 memcpy(as->username, data, size);
182 as->username[size] = 0;
183 } else if (strncmp(key, "password", 9) == 0) {
184 if (size > 32) size = 32;
185 memcpy(as->password, data, size);
186 as->password[size] = 0;
192 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) {
193 struct put_state *ps = cls;
195 if (strncmp(key, "data", 5) == 0) {
196 if (ps->data == NULL) {
197 ps->data_size = size;
198 ps->data = malloc(size);
200 if (ps->data_size + size > MAX_RECORD_SIZE) {
201 size = MAX_RECORD_SIZE - ps->data_size;
203 ps->data_size += size;
204 ps->data = realloc(ps->data, ps->data_size);
206 memcpy(ps->data + off, data, size);
207 if (ps->data_size == MAX_RECORD_SIZE)
209 } else if (strncmp(key, "username", 9) == 0) {
210 if (size > 32) size = 32;
211 memcpy(ps->username, data, size);
212 ps->username[size] = 0;
218 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) {
219 struct subscribe_state *ss = cls;
221 if (strncmp(key, "username", 9) == 0) {
222 if (size > 32) size = 32;
223 memcpy(ss->username, data, size);
224 ss->username[size] = 0;
225 } else if (strncmp(key, "to", 3) == 0) {
226 if (size > 32) size = 32;
227 memcpy(ss->to, data, size);
234 struct MHD_Response *create_response_for_range(struct blerg *b, uint64_t from, uint64_t to) {
235 struct MHD_Response *response;
236 struct get_state *gs = malloc(sizeof(struct get_state));
239 uint64_t record_count = blerg_get_record_count(b);
241 if (from > to || from >= record_count || to >= record_count || to - from > 99) {
247 gs->entries = make_sequential_list(from, to);
250 gs->g = yajl_gen_alloc(&yajl_c, NULL);
251 gs->yoff = gs->done = 0;
253 response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
258 struct MHD_Response *create_tag_response(struct tag *results, uint64_t len) {
259 struct tag_state *ts = malloc(sizeof(struct tag_state));
260 ts->g = yajl_gen_alloc(&yajl_c, NULL);
261 ts->results = results;
263 ts->yoff = ts->done = 0;
265 return MHD_create_response_from_callback(-1, 262144, &GET_generate_taglist, ts, &GET_generate_taglist_free);
269 ahc_derp (void *cls, struct MHD_Connection *connection, const char *url, const char *method,
270 const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) {
271 struct MHD_Response *response;
273 struct url_info info;
276 if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
278 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
279 return respond_405(connection);
286 return respond_404(connection);
288 ret = parse_url_info(url + 5, &info);
289 if ((ret & URL_INFO_NAME) == 0)
290 return respond_404(connection);
292 if (!blerg_exists(info.name))
293 return respond_404(connection);
297 struct blerg *b = blerg_open(info.name);
299 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
300 response = create_response_for_range(b, info.record, info.record_to);
301 } else if (ret & URL_INFO_RECORD) {
302 ret = blerg_fetch(b, info.record, &data, &len);
306 return respond_404(connection);
307 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
309 uint64_t record_count, from, to;
310 record_count = blerg_get_record_count(b);
311 if (record_count == 0) {
313 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
315 to = record_count - 1;
316 from = (record_count > 50 ? to - 49 : 0);
317 response = create_response_for_range(b, from, to);
321 if (response == NULL) {
323 return respond_JSON_Failure(connection);
326 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
327 MHD_destroy_response(response);
329 } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
331 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
332 return respond_405(connection);
339 return respond_404(connection);
341 ret = parse_url_info(url + 5, &info);
342 if ((ret & URL_INFO_NAME) == 0)
343 return respond_404(connection);
345 if (!tag_exists(info.name))
346 return respond_404(connection);
349 struct tag *taglist = tag_list(info.name, 0, &recs, -1);
352 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
354 response = create_tag_response(taglist, recs);
357 if (response == NULL)
358 return respond_JSON_Failure(connection);
360 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
361 MHD_destroy_response(response);
364 } else if (strncmp(url, "/put", 4) == 0) {
365 struct put_state *ps = (struct put_state *) *ptr;
367 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
368 return respond_405(connection);
371 return respond_404(connection);
375 struct put_state *ps = malloc(sizeof(struct put_state));
378 ps->pp = MHD_create_post_processor(connection, 16384, &POST_put_iterator, ps);
384 if (*upload_data_size) {
385 MHD_post_process(ps->pp, upload_data, *upload_data_size);
386 *upload_data_size = 0;
390 if (ps->data == NULL || ps->data_size == 0 || ps->username[0] == 0)
391 return respond_JSON_Failure(connection);
393 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
394 if (!auth_check_token(ps->username, given_token))
395 return respond_JSON_Failure(connection);
397 struct blerg *b = blerg_open(ps->username);
399 return respond_JSON_Failure(connection);
400 ret = blerg_store(b, ps->data, ps->data_size);
403 return respond_JSON_Failure(connection);
405 MHD_destroy_post_processor(ps->pp);
410 return respond_JSON_Success(connection);
411 } else if (strncmp(url, "/info", 5) == 0) {
415 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
416 return respond_405(connection);
422 return respond_404(connection);
424 ret = parse_url_info(url + 6, &info);
425 if ((ret & URL_INFO_NAME) == 0)
426 return respond_404(connection);
428 if (!blerg_exists(info.name))
429 return respond_404(connection);
433 struct blerg *b = blerg_open(info.name);
434 uint64_t record_count = blerg_get_record_count(b);
438 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
439 yajl_gen_map_open(g);
440 yajl_gen_string(g, "record_count", 12);
441 snprintf(number, 21, "%llu", record_count);
442 yajl_gen_string(g, number, strlen(number));
443 yajl_gen_map_close(g);
445 const unsigned char *ybuf;
446 yajl_gen_get_buf(g, &ybuf, &len);
448 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
449 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
450 MHD_destroy_response(response);
455 } else if (strncmp(url, "/create", 8) == 0) {
456 struct auth_state *as = (struct auth_state *) *ptr;
459 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
460 return respond_405(connection);
462 struct auth_state *as = malloc(sizeof(struct auth_state));
463 as->username[0] = as->password[0] = 0;
464 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
469 if (*upload_data_size) {
470 MHD_post_process(as->pp, upload_data, *upload_data_size);
471 *upload_data_size = 0;
475 if (as->username[0] == 0 || as->password[0] == 0)
476 return respond_JSON_Failure(connection);
478 if (blerg_exists(as->username))
479 return respond_JSON_Failure(connection);
481 struct blerg *b = blerg_open(as->username);
483 auth_set_password(as->username, as->password);
485 MHD_destroy_post_processor(as->pp);
489 return respond_JSON_Success(connection);
490 } else if (strncmp(url, "/login", 7) == 0) {
491 struct auth_state *as = (struct auth_state *) *ptr;
494 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
495 return respond_405(connection);
497 struct auth_state *as = malloc(sizeof(struct auth_state));
498 as->username[0] = as->password[0] = 0;
499 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
504 if (*upload_data_size) {
505 MHD_post_process(as->pp, upload_data, *upload_data_size);
506 *upload_data_size = 0;
510 if (as->username[0] == 0 || as->password[0] == 0)
511 return respond_JSON_Failure(connection);
513 if (!auth_login(as->username, as->password))
514 return respond_JSON_Failure(connection);
516 response = MHD_create_response_from_data(strlen(JSON_SUCCESS), JSON_SUCCESS, MHD_NO, MHD_NO);
518 char *token = auth_get_token(as->username);
520 snprintf(data, 512, "auth=%s", token);
521 MHD_add_response_header(response, "Set-Cookie", data);
525 MHD_destroy_post_processor(as->pp);
529 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
530 MHD_destroy_response(response);
533 } else if (strncmp(url, "/logout", 8) == 0) {
534 struct auth_state *as = (struct auth_state *) *ptr;
537 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
538 return respond_405(connection);
540 struct auth_state *as = malloc(sizeof(struct auth_state));
541 as->username[0] = as->password[0] = 0;
542 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
547 if (*upload_data_size) {
548 MHD_post_process(as->pp, upload_data, *upload_data_size);
549 *upload_data_size = 0;
553 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
554 if (auth_check_token(as->username, given_token)) {
555 auth_logout(as->username);
556 return respond_JSON_Success(connection);
558 return respond_JSON_Failure(connection);
560 } else if (strncmp(url, "/subscribe", 11) == 0) {
561 struct subscribe_state *ss = (struct subscribe_state *) *ptr;
564 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
565 return respond_405(connection);
567 struct subscribe_state *ss = malloc(sizeof(struct subscribe_state));
568 ss->username[0] = ss->to[0] = 0;
569 ss->pp = MHD_create_post_processor(connection, 1024, &POST_subscribe_iterator, ss);
574 if (*upload_data_size) {
575 MHD_post_process(ss->pp, upload_data, *upload_data_size);
576 *upload_data_size = 0;
580 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
581 if (auth_check_token(ss->username, given_token)) {
582 subscription_add(ss->username, ss->to);
583 return respond_JSON_Success(connection);
585 return respond_JSON_Failure(connection);
588 return respond_404(connection);
593 int main(int argc, char *argv[]) {
594 struct MHD_Daemon *daemon;
600 daemon = MHD_start_daemon(MHD_USE_DEBUG, HTTP_BLERG_PORT, NULL, NULL, &ahc_derp, NULL, MHD_OPTION_END);
601 if (daemon == NULL) {
602 fprintf(stderr, "Could not start web server\n");
607 FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es);
608 if (MHD_get_fdset(daemon, &rs, &ws, &es, &max) != MHD_YES) {
609 fprintf(stderr, "Fatal error getting fd sets\n");
612 select(max + 1, &rs, &ws, &es, NULL);
615 MHD_stop_daemon(daemon);