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;
48 ssize_t GET_generate_list(void *cls, uint64_t pos, char *buf, size_t max) {
49 struct get_state *gs = cls;
50 const unsigned char *ybuf;
56 yajl_gen_get_buf(gs->g, &ybuf, &len);
57 size_t bytes_remaining = len - gs->yoff;
58 if (bytes_remaining > max) {
59 memcpy(buf, ybuf + gs->yoff, max);
63 memcpy(buf, ybuf + gs->yoff, bytes_remaining);
65 yajl_gen_clear(gs->g);
66 return bytes_remaining;
73 if (pos == 0) { /* Start iterating */
74 yajl_gen_array_open(gs->g);
77 /* Snarf one record */
78 json_generate_one_record(gs->g, NULL, gs->b, gs->entries[gs->i]);
81 yajl_gen_array_close(gs->g);
87 yajl_gen_get_buf(gs->g, &ybuf, &len);
89 memcpy(buf, ybuf, max);
93 memcpy(buf, ybuf, len);
94 yajl_gen_clear(gs->g);
99 void GET_generate_list_free(void *cls) {
100 struct get_state *gs = cls;
103 yajl_gen_free(gs->g);
108 ssize_t GET_generate_taglist(void *cls, uint64_t pos, char *buf, size_t max) {
109 struct tag_state *ts = cls;
111 const unsigned char *ybuf;
115 yajl_gen_get_buf(ts->g, &ybuf, &len);
116 size_t bytes_remaining = len - ts->yoff;
117 if (bytes_remaining > max) {
118 memcpy(buf, ybuf + ts->yoff, max);
122 memcpy(buf, ybuf + ts->yoff, bytes_remaining);
124 yajl_gen_clear(ts->g);
125 return bytes_remaining;
132 if (pos == 0) { /* Start iterating */
133 yajl_gen_array_open(ts->g);
136 /* Snarf one record */
137 b = blerg_open(ts->results[ts->i].author);
139 json_generate_one_record(ts->g, ts->results[ts->i].author, b, ts->results[ts->i].record);
144 yajl_gen_array_close(ts->g);
150 yajl_gen_get_buf(ts->g, &ybuf, &len);
152 memcpy(buf, ybuf, max);
156 memcpy(buf, ybuf, len);
157 yajl_gen_clear(ts->g);
162 void GET_generate_taglist_free(void *cls) {
163 struct tag_state *ts = cls;
165 yajl_gen_free(ts->g);
170 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) {
171 struct auth_state *as = cls;
173 if (strncmp(key, "username", 9) == 0) {
174 if (size > 32) size = 32;
175 memcpy(as->username, data, size);
176 as->username[size] = 0;
177 } else if (strncmp(key, "password", 9) == 0) {
178 if (size > 32) size = 32;
179 memcpy(as->password, data, size);
180 as->password[size] = 0;
186 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) {
187 struct put_state *ps = cls;
189 if (strncmp(key, "data", 5) == 0) {
190 if (ps->data == NULL) {
191 ps->data_size = size;
192 ps->data = malloc(size);
194 if (ps->data_size + size > MAX_RECORD_SIZE) {
195 size = MAX_RECORD_SIZE - ps->data_size;
197 ps->data_size += size;
198 ps->data = realloc(ps->data, ps->data_size);
200 memcpy(ps->data + off, data, size);
201 if (ps->data_size == MAX_RECORD_SIZE)
203 } else if (strncmp(key, "username", 9) == 0) {
204 if (size > 32) size = 32;
205 memcpy(ps->username, data, size);
206 ps->username[size] = 0;
212 struct MHD_Response *create_response_for_range(struct blerg *b, uint64_t from, uint64_t to) {
213 struct MHD_Response *response;
214 struct get_state *gs = malloc(sizeof(struct get_state));
217 uint64_t record_count = blerg_get_record_count(b);
219 if (from > to || from >= record_count || to >= record_count || to - from > 99) {
225 gs->entries = make_sequential_list(from, to);
228 gs->g = yajl_gen_alloc(&yajl_c, NULL);
229 gs->yoff = gs->done = 0;
231 response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
236 struct MHD_Response *create_tag_response(struct tag *results, uint64_t len) {
237 struct tag_state *ts = malloc(sizeof(struct tag_state));
238 ts->g = yajl_gen_alloc(&yajl_c, NULL);
239 ts->results = results;
241 ts->yoff = ts->done = 0;
243 return MHD_create_response_from_callback(-1, 262144, &GET_generate_taglist, ts, &GET_generate_taglist_free);
247 ahc_derp (void *cls, struct MHD_Connection *connection, const char *url, const char *method,
248 const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) {
249 struct MHD_Response *response;
251 struct url_info info;
254 if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
256 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
257 return respond_405(connection);
264 return respond_404(connection);
266 ret = parse_url_info(url + 5, &info);
267 if ((ret & URL_INFO_AUTHOR) == 0)
268 return respond_404(connection);
270 if (!blerg_exists(info.author))
271 return respond_404(connection);
275 struct blerg *b = blerg_open(info.author);
277 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
278 response = create_response_for_range(b, info.record, info.record_to);
279 } else if (ret & URL_INFO_RECORD) {
280 ret = blerg_fetch(b, info.record, &data, &len);
284 return respond_404(connection);
285 response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
287 uint64_t record_count, from, to;
288 record_count = blerg_get_record_count(b);
289 if (record_count == 0) {
291 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
293 to = record_count - 1;
294 from = (record_count > 50 ? to - 49 : 0);
295 response = create_response_for_range(b, from, to);
299 if (response == NULL) {
301 return respond_JSON_Failure(connection);
304 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
305 MHD_destroy_response(response);
307 } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
309 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
310 return respond_405(connection);
317 return respond_404(connection);
319 ret = parse_url_info(url + 5, &info);
320 if ((ret & URL_INFO_AUTHOR) == 0)
321 return respond_404(connection);
323 if (!tag_exists(info.author))
324 return respond_404(connection);
327 struct tag *taglist = tag_list(info.author, 0, &recs, -1);
330 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
332 response = create_tag_response(taglist, recs);
335 if (response == NULL)
336 return respond_JSON_Failure(connection);
338 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
339 MHD_destroy_response(response);
342 } else if (strncmp(url, "/put", 4) == 0) {
343 struct put_state *ps = (struct put_state *) *ptr;
345 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
346 return respond_405(connection);
349 return respond_404(connection);
353 struct put_state *ps = malloc(sizeof(struct put_state));
356 ps->pp = MHD_create_post_processor(connection, 16384, &POST_put_iterator, ps);
362 if (*upload_data_size) {
363 MHD_post_process(ps->pp, upload_data, *upload_data_size);
364 *upload_data_size = 0;
368 if (ps->data == NULL || ps->data_size == 0 || ps->username[0] == 0)
369 return respond_JSON_Failure(connection);
371 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
372 if (!auth_check_token(ps->username, given_token))
373 return respond_JSON_Failure(connection);
375 struct blerg *b = blerg_open(ps->username);
377 return respond_JSON_Failure(connection);
378 ret = blerg_store(b, ps->data, ps->data_size);
381 return respond_JSON_Failure(connection);
383 MHD_destroy_post_processor(ps->pp);
388 return respond_JSON_Success(connection);
389 } else if (strncmp(url, "/info", 5) == 0) {
393 if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
394 return respond_405(connection);
400 return respond_404(connection);
402 ret = parse_url_info(url + 6, &info);
403 if ((ret & URL_INFO_AUTHOR) == 0)
404 return respond_404(connection);
406 if (!blerg_exists(info.author))
407 return respond_404(connection);
411 struct blerg *b = blerg_open(info.author);
412 uint64_t record_count = blerg_get_record_count(b);
416 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
417 yajl_gen_map_open(g);
418 yajl_gen_string(g, "record_count", 12);
419 snprintf(number, 21, "%llu", record_count);
420 yajl_gen_string(g, number, strlen(number));
421 yajl_gen_map_close(g);
423 const unsigned char *ybuf;
424 yajl_gen_get_buf(g, &ybuf, &len);
426 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
427 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
428 MHD_destroy_response(response);
433 } else if (strncmp(url, "/create", 8) == 0) {
434 struct auth_state *as = (struct auth_state *) *ptr;
437 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
438 return respond_405(connection);
440 struct auth_state *as = malloc(sizeof(struct auth_state));
441 as->username[0] = as->password[0] = 0;
442 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
447 if (*upload_data_size) {
448 MHD_post_process(as->pp, upload_data, *upload_data_size);
449 *upload_data_size = 0;
453 if (as->username[0] == 0 || as->password[0] == 0)
454 return respond_JSON_Failure(connection);
456 if (blerg_exists(as->username))
457 return respond_JSON_Failure(connection);
459 struct blerg *b = blerg_open(as->username);
461 auth_set_password(as->username, as->password);
463 MHD_destroy_post_processor(as->pp);
467 return respond_JSON_Success(connection);
468 } else if (strncmp(url, "/login", 7) == 0) {
469 struct auth_state *as = (struct auth_state *) *ptr;
472 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
473 return respond_405(connection);
475 struct auth_state *as = malloc(sizeof(struct auth_state));
476 as->username[0] = as->password[0] = 0;
477 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
482 if (*upload_data_size) {
483 MHD_post_process(as->pp, upload_data, *upload_data_size);
484 *upload_data_size = 0;
488 if (as->username[0] == 0 || as->password[0] == 0)
489 return respond_JSON_Failure(connection);
491 if (!auth_login(as->username, as->password))
492 return respond_JSON_Failure(connection);
494 response = MHD_create_response_from_data(strlen(JSON_SUCCESS), JSON_SUCCESS, MHD_NO, MHD_NO);
496 char *token = auth_get_token(as->username);
498 snprintf(data, 512, "auth=%s", token);
499 MHD_add_response_header(response, "Set-Cookie", data);
503 MHD_destroy_post_processor(as->pp);
507 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
508 MHD_destroy_response(response);
511 } else if (strncmp(url, "/logout", 8) == 0) {
512 struct auth_state *as = (struct auth_state *) *ptr;
515 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
516 return respond_405(connection);
518 struct auth_state *as = malloc(sizeof(struct auth_state));
519 as->username[0] = as->password[0] = 0;
520 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
525 if (*upload_data_size) {
526 MHD_post_process(as->pp, upload_data, *upload_data_size);
527 *upload_data_size = 0;
531 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
532 if (auth_check_token(as->username, given_token)) {
533 auth_logout(as->username);
534 return respond_JSON_Success(connection);
536 return respond_JSON_Failure(connection);
539 return respond_404(connection);
544 int main(int argc, char *argv[]) {
545 struct MHD_Daemon *daemon;
551 daemon = MHD_start_daemon(MHD_USE_DEBUG, 8080, NULL, NULL, &ahc_derp, NULL, MHD_OPTION_END);
552 if (daemon == NULL) {
553 fprintf(stderr, "Could not start web server\n");
558 FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es);
559 if (MHD_get_fdset(daemon, &rs, &ws, &es, &max) != MHD_YES) {
560 fprintf(stderr, "Fatal error getting fd sets\n");
563 select(max + 1, &rs, &ws, &es, NULL);
566 MHD_stop_daemon(daemon);