Add /feedinfo endpoint
[blerg.git] / http / http_blerg.c
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.
3  */
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <microhttpd.h>
8 #include <yajl/yajl_gen.h>
9 #include "database.h"
10 #include "tags.h"
11 #include "subscription.h"
12 #include "auth.h"
13 #include "canned_responses.h"
14 #include "app.h"
15 #include "config.h"
16
17 yajl_gen_config yajl_c = { 0, 0 };
18
19 struct auth_state {
20         struct MHD_PostProcessor *pp;
21         char username[33];
22         char password[33];
23 };
24
25 struct put_state {
26         struct MHD_PostProcessor *pp;
27         char username[33];
28         char *data;
29         int data_size;
30 };
31
32 struct subscribe_state {
33         struct MHD_PostProcessor *pp;
34         char username[33];
35         char to[33];
36 };
37
38 struct get_state {
39         struct blerg *b;
40         yajl_gen g;
41         unsigned int yoff;
42         uint64_t *entries;
43         uint64_t i;
44         int done;
45 };
46
47 struct blergref_state {
48         yajl_gen g;
49         unsigned int yoff;
50         struct blergref *results;
51         uint64_t i;
52         int done;
53 };
54
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;
58         char *data;
59         char number[21];
60         unsigned int len;
61
62         if (gs->yoff > 0) {
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);
67                         gs->yoff += max;
68                         return max;
69                 } else {
70                         memcpy(buf, ybuf + gs->yoff, bytes_remaining);
71                         gs->yoff = 0;
72                         yajl_gen_clear(gs->g);
73                         return bytes_remaining;
74                 }
75         }
76
77         if (gs->done)
78                 return -1;
79
80         if (pos == 0) { /* Start iterating */
81                 yajl_gen_array_open(gs->g);
82         }
83
84         /* Snarf one record */
85         json_generate_one_record(gs->g, NULL, gs->b, gs->entries[gs->i], 0);
86
87         if (gs->i == 0) {
88                 yajl_gen_array_close(gs->g);
89                 gs->done = 1;
90         }
91         gs->i--;
92
93
94         yajl_gen_get_buf(gs->g, &ybuf, &len);
95         if (len > max) {
96                 memcpy(buf, ybuf, max);
97                 gs->yoff = max;
98                 return max;
99         } else {
100                 memcpy(buf, ybuf, len);
101                 yajl_gen_clear(gs->g);
102                 return len;
103         }
104 }
105
106 void GET_generate_list_free(void *cls) {
107         struct get_state *gs = cls;
108
109         blerg_close(gs->b);
110         yajl_gen_free(gs->g);
111         free(gs->entries);
112         free(gs);
113 }
114
115 ssize_t GET_generate_blergref_list(void *cls, uint64_t pos, char *buf, size_t max) {
116         struct blergref_state *bs = cls;
117         struct blerg *b;
118         const unsigned char *ybuf;
119         unsigned int len;
120
121         if (bs->yoff > 0) {
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);
126                         bs->yoff += max;
127                         return max;
128                 } else {
129                         memcpy(buf, ybuf + bs->yoff, bytes_remaining);
130                         bs->yoff = 0;
131                         yajl_gen_clear(bs->g);
132                         return bytes_remaining;
133                 }
134         }
135
136         if (bs->done)
137                 return -1;
138
139         if (pos == 0) { /* Start iterating */
140                 yajl_gen_array_open(bs->g);
141         }
142
143         /* Snarf one record */
144         b = blerg_open(bs->results[bs->i].author);
145         if (b != NULL) {
146                 json_generate_one_record(bs->g, bs->results[bs->i].author, b, bs->results[bs->i].record, 0);
147                 blerg_close(b);
148         }
149
150         if (bs->i == 0) {
151                 yajl_gen_array_close(bs->g);
152                 bs->done = 1;
153         }
154
155         bs->i--;
156
157         yajl_gen_get_buf(bs->g, &ybuf, &len);
158         if (len > max) {
159                 memcpy(buf, ybuf, max);
160                 bs->yoff = max;
161                 return max;
162         } else {
163                 memcpy(buf, ybuf, len);
164                 yajl_gen_clear(bs->g);
165                 return len;
166         }
167 }
168
169 void GET_generate_blergref_list_free(void *cls) {
170         struct blergref_state *bs = cls;
171
172         yajl_gen_free(bs->g);
173         free(bs->results);
174         free(bs);
175 }
176
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;
179
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;
188         }
189
190         return MHD_YES;
191 }
192
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;
195
196         if (strncmp(key, "data", 5) == 0) {
197                 if (ps->data == NULL) {
198                         ps->data_size = size;
199                         ps->data = malloc(size);
200                 } else {
201                         if (ps->data_size + size > MAX_RECORD_SIZE) {
202                                 size = MAX_RECORD_SIZE - ps->data_size;
203                         }
204                         ps->data_size += size;
205                         ps->data = realloc(ps->data, ps->data_size);
206                 }
207                 memcpy(ps->data + off, data, size);
208                 if (ps->data_size == MAX_RECORD_SIZE)
209                         return MHD_NO;
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;
214         }
215
216         return MHD_YES;
217 }
218
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;
221
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);
229                 ss->to[size] = 0;
230         }
231
232         return MHD_YES;
233 }
234
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));
238         gs->b = b;
239
240         uint64_t record_count = blerg_get_record_count(b);
241
242         if (from > to || from >= record_count || to >= record_count || to - from > 99) {
243                 blerg_close(b);
244                 free(gs);
245                 return NULL;
246         }
247
248         gs->entries = make_sequential_list(from, to);
249         gs->i = to - from;
250
251         gs->g = yajl_gen_alloc(&yajl_c, NULL);
252         gs->yoff = gs->done = 0;
253
254         response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
255
256         return response;
257 }
258
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;
263         bs->i = len - 1;
264         bs->yoff = bs->done = 0;
265
266         return MHD_create_response_from_callback(-1, 262144, &GET_generate_blergref_list, bs, &GET_generate_blergref_list_free);
267 }
268
269 int check_auth(struct MHD_Connection *connection, const char *method, const char *upload_data, size_t *upload_data_size, void **ptr) {
270         struct auth_state *as = (struct auth_state *) *ptr;
271         if (as == NULL) {
272                 if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
273                         return respond_405(connection);
274
275                 as = malloc(sizeof(struct auth_state));
276                 as->username[0] = as->password[0] = 0;
277                 as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
278                 *ptr = as;
279                 return MHD_YES;
280         }
281
282         if (*upload_data_size) {
283                 MHD_post_process(as->pp, upload_data, *upload_data_size);
284                 *upload_data_size = 0;
285                 return MHD_YES;
286         }
287
288         const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
289         if (!auth_check_token(as->username, given_token))
290                 return respond_403(connection);
291
292         return MHD_NO;
293 }
294
295 static int
296 ahc_derp (void *cls, struct MHD_Connection *connection, const char *url, const char *method,
297           const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) {
298         struct MHD_Response *response;
299         int ret, len;
300         struct url_info info;
301         char *data;
302
303         if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
304                 if (*ptr == NULL) {
305                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
306                                 return respond_405(connection);
307
308                         *ptr = (void *) 1;
309                         return MHD_YES;
310                 }
311
312                 if (url[4] != '/')
313                         return respond_404(connection);
314
315                 ret = parse_url_info(url + 5, &info);
316                 if ((ret & URL_INFO_NAME) == 0)
317                         return respond_404(connection);
318
319                 if (!blerg_exists(info.name))
320                         return respond_404(connection);
321
322                 *ptr == NULL;
323
324                 struct blerg *b = blerg_open(info.name);
325
326                 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
327                         response = create_response_for_range(b, info.record, info.record_to);
328                 } else if (ret & URL_INFO_RECORD) {
329                         ret = blerg_fetch(b, info.record, &data, &len);
330                         blerg_close(b);
331
332                         if (ret == 0)
333                                 return respond_404(connection);
334                         response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
335                 } else {
336                         uint64_t record_count, from, to;
337                         record_count = blerg_get_record_count(b);
338                         if (record_count == 0) {
339                                 blerg_close(b);
340                                 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
341                         } else {
342                                 to = record_count - 1;
343                                 from = (record_count > 50 ? to - 49 : 0);
344                                 response = create_response_for_range(b, from, to);
345                         }
346                 }
347
348                 if (response == NULL) {
349                         blerg_close(b);
350                         return respond_JSON_Failure(connection);
351                 }
352
353                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
354                 MHD_destroy_response(response);
355                 return ret;
356         } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
357                 if (*ptr == NULL) {
358                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
359                                 return respond_405(connection);
360
361                         *ptr = (void *) 1;
362                         return MHD_YES;
363                 }
364
365                 if (url[4] != '/')
366                         return respond_404(connection);
367
368                 ret = parse_url_info(url + 5, &info);
369                 if ((ret & URL_INFO_NAME) == 0)
370                         return respond_404(connection);
371
372                 if (info.name[0] == 'H')
373                         info.name[0] = '#';
374                 if (!tag_exists(info.name))
375                         return respond_404(connection);
376
377                 int recs = 50;
378                 struct blergref *taglist = tag_list(info.name, 0, &recs, -1);
379
380                 if (recs == 0) {
381                         response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
382                 } else {
383                         response = create_blergref_response(taglist, recs);
384                 }
385
386                 if (response == NULL)
387                         return respond_JSON_Failure(connection);
388
389                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
390                 MHD_destroy_response(response);
391
392                 return ret;
393         } else if (strncmp(url, "/put", 4) == 0) {
394                 struct put_state *ps = (struct put_state *) *ptr;
395                 if (*ptr == NULL) {
396                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
397                                 return respond_405(connection);
398
399                         if (url[4] == '/')
400                                 return respond_404(connection);
401
402                         *ptr = (void *) 1;
403
404                         struct put_state *ps = malloc(sizeof(struct put_state));
405                         ps->data = NULL;
406                         ps->data_size = 0;
407                         ps->pp = MHD_create_post_processor(connection, 16384, &POST_put_iterator, ps);
408                         ps->username[0] = 0;
409                         *ptr = ps;
410                         return MHD_YES;
411                 }
412
413                 if (*upload_data_size) {
414                         MHD_post_process(ps->pp, upload_data, *upload_data_size);
415                         *upload_data_size = 0;
416                         return MHD_YES;
417                 }
418
419                 if (ps->data == NULL || ps->data_size == 0 || ps->username[0] == 0)
420                         return respond_JSON_Failure(connection);
421
422                 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
423                 if (!auth_check_token(ps->username, given_token))
424                         return respond_JSON_Failure(connection);
425
426                 struct blerg *b = blerg_open(ps->username);
427                 if (b == NULL)
428                         return respond_JSON_Failure(connection);
429                 ret = blerg_store(b, ps->data, ps->data_size);
430                 blerg_close(b);
431                 if (ret == -1)
432                         return respond_JSON_Failure(connection);
433
434                 MHD_destroy_post_processor(ps->pp);
435                 free(ps->data);
436                 free(ps);
437                 *ptr = NULL;
438
439                 return respond_JSON_Success(connection);
440         } else if (strncmp(url, "/info", 5) == 0) {
441                 if (*ptr == NULL) {
442                         *ptr = (void *) 1;
443
444                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
445                                 return respond_405(connection);
446                         return MHD_YES;
447                 }
448
449
450                 if (url[5] != '/')
451                         return respond_404(connection);
452
453                 ret = parse_url_info(url + 6, &info);
454                 if ((ret & URL_INFO_NAME) == 0)
455                         return respond_404(connection);
456
457                 if (!blerg_exists(info.name))
458                         return respond_404(connection);
459
460                 *ptr == NULL;
461
462                 struct blerg *b = blerg_open(info.name);
463                 uint64_t record_count = blerg_get_record_count(b);
464                 blerg_close(b);
465
466                 char number[21];
467                 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
468                 yajl_gen_map_open(g);
469                 yajl_gen_string(g, "record_count", 12);
470                 snprintf(number, 21, "%llu", record_count);
471                 yajl_gen_string(g, number, strlen(number));
472                 yajl_gen_map_close(g);
473
474                 const unsigned char *ybuf;
475                 yajl_gen_get_buf(g, &ybuf, &len);
476
477                 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
478                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
479                 MHD_destroy_response(response);
480
481                 yajl_gen_free(g);
482
483                 return ret;
484         } else if (strncmp(url, "/create", 8) == 0) {
485                 struct auth_state *as = (struct auth_state *) *ptr;
486
487                 if (as == NULL) {
488                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
489                                 return respond_405(connection);
490
491                         struct auth_state *as = malloc(sizeof(struct auth_state));
492                         as->username[0] = as->password[0] = 0;
493                         as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
494                         *ptr = as;
495                         return MHD_YES;
496                 }
497
498                 if (*upload_data_size) {
499                         MHD_post_process(as->pp, upload_data, *upload_data_size);
500                         *upload_data_size = 0;
501                         return MHD_YES;
502                 }
503
504                 if (as->username[0] == 0 || as->password[0] == 0)
505                         return respond_JSON_Failure(connection);
506
507                 if (blerg_exists(as->username))
508                         return respond_JSON_Failure(connection);
509
510                 struct blerg *b = blerg_open(as->username);
511                 blerg_close(b);
512                 auth_set_password(as->username, as->password);
513
514                 MHD_destroy_post_processor(as->pp);
515                 free(as);
516                 *ptr = NULL;
517
518                 return respond_JSON_Success(connection);
519         } else if (strncmp(url, "/login", 7) == 0) {
520                 struct auth_state *as = (struct auth_state *) *ptr;
521
522                 if (as == NULL) {
523                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
524                                 return respond_405(connection);
525
526                         struct auth_state *as = malloc(sizeof(struct auth_state));
527                         as->username[0] = as->password[0] = 0;
528                         as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
529                         *ptr = as;
530                         return MHD_YES;
531                 }
532
533                 if (*upload_data_size) {
534                         MHD_post_process(as->pp, upload_data, *upload_data_size);
535                         *upload_data_size = 0;
536                         return MHD_YES;
537                 }
538
539                 if (as->username[0] == 0 || as->password[0] == 0)
540                         return respond_JSON_Failure(connection);
541
542                 if (!auth_login(as->username, as->password))
543                         return respond_JSON_Failure(connection);
544
545                 response = MHD_create_response_from_data(strlen(JSON_SUCCESS), JSON_SUCCESS, MHD_NO, MHD_NO);
546
547                 char *token = auth_get_token(as->username);
548                 data = malloc(512);
549                 snprintf(data, 512, "auth=%s", token);
550                 MHD_add_response_header(response, "Set-Cookie", data);
551                 free(token);
552                 free(data);
553
554                 MHD_destroy_post_processor(as->pp);
555                 free(as);
556                 *ptr = NULL;
557
558                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
559                 MHD_destroy_response(response);
560
561                 return ret;
562         } else if (strncmp(url, "/logout", 8) == 0) {
563                 struct auth_state *as = (struct auth_state *) *ptr;
564
565                 if (as == NULL) {
566                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
567                                 return respond_405(connection);
568
569                         struct auth_state *as = malloc(sizeof(struct auth_state));
570                         as->username[0] = as->password[0] = 0;
571                         as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
572                         *ptr = as;
573                         return MHD_YES;
574                 }
575
576                 if (*upload_data_size) {
577                         MHD_post_process(as->pp, upload_data, *upload_data_size);
578                         *upload_data_size = 0;
579                         return MHD_YES;
580                 }
581
582                 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
583                 if (auth_check_token(as->username, given_token)) {
584                         auth_logout(as->username);
585                         return respond_JSON_Success(connection);
586                 } else {
587                         return respond_JSON_Failure(connection);
588                 }
589         } else if (strncmp(url, "/subscribe", 11) == 0 || strncmp(url, "/unsubscribe", 13) == 0) {
590                 struct subscribe_state *ss = (struct subscribe_state *) *ptr;
591
592                 if (ss == NULL) {
593                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
594                                 return respond_405(connection);
595
596                         struct subscribe_state *ss = malloc(sizeof(struct subscribe_state));
597                         ss->username[0] = ss->to[0] = 0;
598                         ss->pp = MHD_create_post_processor(connection, 1024, &POST_subscribe_iterator, ss);
599                         *ptr = ss;
600                         return MHD_YES;
601                 }
602
603                 if (*upload_data_size) {
604                         MHD_post_process(ss->pp, upload_data, *upload_data_size);
605                         *upload_data_size = 0;
606                         return MHD_YES;
607                 }
608
609                 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
610                 if (auth_check_token(ss->username, given_token)) {
611                         if (url[1] == 'u') {
612                                 subscription_remove(ss->username, ss->to);
613                         } else {
614                                 subscription_add(ss->username, ss->to);
615                         }
616                         return respond_JSON_Success(connection);
617                 } else {
618                         return respond_JSON_Failure(connection);
619                 }
620         } else if (strncmp(url, "/feed", 6) == 0) {
621                 struct auth_state *as = (struct auth_state *) *ptr;
622
623                 if (as == NULL) {
624                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
625                                 return respond_405(connection);
626
627                         struct auth_state *as = malloc(sizeof(struct auth_state));
628                         as->username[0] = as->password[0] = 0;
629                         as->pp = MHD_create_post_processor(connection, 1024, &POST_auth_iterator, as);
630                         *ptr = as;
631                         return MHD_YES;
632                 }
633
634                 if (*upload_data_size) {
635                         MHD_post_process(as->pp, upload_data, *upload_data_size);
636                         *upload_data_size = 0;
637                         return MHD_YES;
638                 }
639
640                 const char *given_token = MHD_lookup_connection_value(connection, MHD_COOKIE_KIND, "auth");
641                 if (auth_check_token(as->username, given_token)) {
642                         int recs = 50;
643                         struct blergref *feedlist = subscription_list(as->username, 0, &recs, -1);
644
645                         if (recs == 0) {
646                                 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
647                         } else {
648                                 response = create_blergref_response(feedlist, recs);
649                         }
650
651                         if (response == NULL)
652                                 return respond_JSON_Failure(connection);
653
654                         ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
655                         MHD_destroy_response(response);
656
657                         return ret;
658                 } else {
659                         return respond_JSON_Failure(connection);
660                 }
661         } else if (strncmp(url, "/feedinfo", 9) == 0) {
662                 ret = check_auth(connection, method, upload_data, upload_data_size, ptr);
663                 if (ret == MHD_YES)
664                         return MHD_YES;
665
666                 struct auth_state *as = (struct auth_state *) *ptr;
667
668                 if (url[9] != '/')
669                         return respond_404(connection);
670
671                 ret = parse_url_info(url + 10, &info);
672                 if ((ret & URL_INFO_NAME) == 0)
673                         return respond_404(connection);
674
675                 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
676                 yajl_gen_map_open(g);
677                 yajl_gen_string(g, "subscribed", 10);
678                 yajl_gen_bool(g, is_subscribed(as->username, info.name));
679                 yajl_gen_map_close(g);
680
681                 const unsigned char *ybuf;
682                 yajl_gen_get_buf(g, &ybuf, &len);
683
684                 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
685                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
686                 MHD_destroy_response(response);
687
688                 yajl_gen_free(g);
689                 free(as);
690
691                 return ret;
692         } else {
693                 return respond_404(connection);
694         }
695 }
696
697
698 int main(int argc, char *argv[]) {
699         struct MHD_Daemon *daemon;
700         fd_set rs, ws, es;
701         int max;
702
703         init_responses();
704
705         daemon = MHD_start_daemon(MHD_USE_DEBUG, HTTP_BLERG_PORT, NULL, NULL, &ahc_derp, NULL, MHD_OPTION_END);
706         if (daemon == NULL) {
707                 fprintf(stderr, "Could not start web server\n");
708                 return 1;
709         }
710
711         while (1) {
712                 FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es);
713                 if (MHD_get_fdset(daemon, &rs, &ws, &es, &max) != MHD_YES) {
714                         fprintf(stderr, "Fatal error getting fd sets\n");
715                         break;
716                 }
717                 select(max + 1, &rs, &ws, &es, NULL);
718                 MHD_run(daemon);
719         }
720         MHD_stop_daemon(daemon);
721         return 0;
722 }