Refactor a bunch of stuff for cgi fork
[blerg.git] / http / http_blerg.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <microhttpd.h>
5 #include <yajl/yajl_gen.h>
6 #include "database.h"
7 #include "tags.h"
8 #include "auth.h"
9 #include "canned_responses.h"
10 #include "app.h"
11 #include "config.h"
12
13 yajl_gen_config yajl_c = { 0, 0 };
14
15 struct create_state {
16         struct MHD_PostProcessor *pp;
17         char username[33];
18         char password[33];
19 };
20
21 struct put_state {
22         struct MHD_PostProcessor *pp;
23         char *data;
24         int data_size;
25 };
26
27 struct get_state {
28         struct blerg *b;
29         yajl_gen g;
30         unsigned int yoff;
31         uint64_t *entries;
32         uint64_t i;
33         int done;
34 };
35
36 struct tag_state {
37         yajl_gen g;
38         unsigned int yoff;
39         struct tag *results;
40         uint64_t i;
41         int done;
42 };
43
44 ssize_t GET_generate_list(void *cls, uint64_t pos, char *buf, size_t max) {
45         struct get_state *gs = cls;
46         const unsigned char *ybuf;
47         char *data;
48         char number[21];
49         unsigned int len;
50
51         if (gs->yoff > 0) {
52                 yajl_gen_get_buf(gs->g, &ybuf, &len);
53                 size_t bytes_remaining = len - gs->yoff;
54                 if (bytes_remaining > max) {
55                         memcpy(buf, ybuf + gs->yoff, max);
56                         gs->yoff += max;
57                         return max;
58                 } else {
59                         memcpy(buf, ybuf + gs->yoff, bytes_remaining);
60                         gs->yoff = 0;
61                         yajl_gen_clear(gs->g);
62                         return bytes_remaining;
63                 }
64         }
65
66         if (gs->done)
67                 return -1;
68
69         if (pos == 0) { /* Start iterating */
70                 yajl_gen_array_open(gs->g);
71         }
72
73         /* Snarf one record */
74         json_generate_one_record(gs->g, NULL, gs->b, gs->entries[gs->i]);
75
76         if (gs->i == 0) {
77                 yajl_gen_array_close(gs->g);
78                 gs->done = 1;
79         }
80         gs->i--;
81
82
83         yajl_gen_get_buf(gs->g, &ybuf, &len);
84         if (len > max) {
85                 memcpy(buf, ybuf, max);
86                 gs->yoff = max;
87                 return max;
88         } else {
89                 memcpy(buf, ybuf, len);
90                 yajl_gen_clear(gs->g);
91                 return len;
92         }
93 }
94
95 void GET_generate_list_free(void *cls) {
96         struct get_state *gs = cls;
97
98         blerg_close(gs->b);
99         yajl_gen_free(gs->g);
100         free(gs->entries);
101         free(gs);
102 }
103
104 ssize_t GET_generate_taglist(void *cls, uint64_t pos, char *buf, size_t max) {
105         struct tag_state *ts = cls;
106         struct blerg *b;
107         const unsigned char *ybuf;
108         unsigned int len;
109
110         if (ts->yoff > 0) {
111                 yajl_gen_get_buf(ts->g, &ybuf, &len);
112                 size_t bytes_remaining = len - ts->yoff;
113                 if (bytes_remaining > max) {
114                         memcpy(buf, ybuf + ts->yoff, max);
115                         ts->yoff += max;
116                         return max;
117                 } else {
118                         memcpy(buf, ybuf + ts->yoff, bytes_remaining);
119                         ts->yoff = 0;
120                         yajl_gen_clear(ts->g);
121                         return bytes_remaining;
122                 }
123         }
124
125         if (ts->done)
126                 return -1;
127
128         if (pos == 0) { /* Start iterating */
129                 yajl_gen_array_open(ts->g);
130         }
131
132         /* Snarf one record */
133         b = blerg_open(ts->results[ts->i].author);
134         if (b == NULL)
135                 goto skip_bad_blerg;
136
137         json_generate_one_record(ts->g, ts->results[ts->i].author, b, ts->results[ts->i].record);
138
139         blerg_close(b);
140
141 skip_bad_blerg:
142         if (ts->i == 0) {
143                 yajl_gen_array_close(ts->g);
144                 ts->done = 1;
145         }
146
147         ts->i--;
148
149         yajl_gen_get_buf(ts->g, &ybuf, &len);
150         if (len > max) {
151                 memcpy(buf, ybuf, max);
152                 ts->yoff = max;
153                 return max;
154         } else {
155                 memcpy(buf, ybuf, len);
156                 yajl_gen_clear(ts->g);
157                 return len;
158         }
159 }
160
161 void GET_generate_taglist_free(void *cls) {
162         struct tag_state *ts = cls;
163
164         yajl_gen_free(ts->g);
165         free(ts->results);
166         free(ts);
167 }
168
169 int POST_create_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) {
170         struct create_state *cs = cls;
171
172         if (strncmp(key, "username", 9) == 0) {
173                 if (size > 32) size = 32;
174                 memcpy(cs->username, data, size);
175                 cs->username[size] = 0;
176         } else if (strncmp(key, "password", 9) == 0) {
177                 if (size > 32) size = 32;
178                 memcpy(cs->password, data, size);
179                 cs->password[size] = 0;
180         }
181
182         return MHD_YES;
183 }
184
185 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) {
186         struct put_state *ps = cls;
187
188         if (strncmp(key, "data", 5) == 0) {
189                 if (ps->data == NULL) {
190                         ps->data_size = size;
191                         ps->data = malloc(size);
192                 } else {
193                         if (ps->data_size + size > MAX_RECORD_SIZE) {
194                                 size = MAX_RECORD_SIZE - ps->data_size;
195                         }
196                         ps->data_size += size;
197                         ps->data = realloc(ps->data, ps->data_size);
198                 }
199                 memcpy(ps->data + off, data, size);
200                 if (ps->data_size == MAX_RECORD_SIZE)
201                         return MHD_NO;
202         }
203
204         return MHD_YES;
205 }
206
207 struct MHD_Response *create_response_for_range(struct blerg *b, uint64_t from, uint64_t to) {
208         struct MHD_Response *response;
209         struct get_state *gs = malloc(sizeof(struct get_state));
210         gs->b = b;
211
212         uint64_t record_count = blerg_get_record_count(b);
213
214         if (from > to || from >= record_count || to >= record_count || to - from > 99) {
215                 blerg_close(b);
216                 free(gs);
217                 return NULL;
218         }
219
220         gs->entries = make_sequential_list(from, to);
221         gs->i = to - from;
222
223         gs->g = yajl_gen_alloc(&yajl_c, NULL);
224         gs->yoff = gs->done = 0;
225
226         response = MHD_create_response_from_callback(-1, 262144, &GET_generate_list, gs, &GET_generate_list_free);
227
228         return response;
229 }
230
231 struct MHD_Response *create_tag_response(struct tag *results, uint64_t len) {
232         struct tag_state *ts = malloc(sizeof(struct tag_state));
233         ts->g = yajl_gen_alloc(&yajl_c, NULL);
234         ts->results = results;
235         ts->i = len - 1;
236         ts->yoff = ts->done = 0;
237
238         return MHD_create_response_from_callback(-1, 262144, &GET_generate_taglist, ts, &GET_generate_taglist_free);
239 }
240
241 static int
242 ahc_derp (void *cls, struct MHD_Connection *connection, const char *url, const char *method,
243           const char *version, const char *upload_data, size_t *upload_data_size, void **ptr) {
244         struct MHD_Response *response;
245         int ret, len;
246         struct url_info info;
247         char *data;
248
249         if (strncmp(url, "/get", 4) == 0 && strlen(url) > 4) {
250                 if (*ptr == NULL) {
251                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
252                                 return respond_405(connection);
253
254                         *ptr = (void *) 1;
255                         return MHD_YES;
256                 }
257
258                 if (url[4] != '/')
259                         return respond_404(connection);
260
261                 ret = parse_url_info(url + 5, &info);
262                 if ((ret & URL_INFO_AUTHOR) == 0)
263                         return respond_404(connection);
264
265                 if (!blerg_exists(info.author))
266                         return respond_404(connection);
267
268                 *ptr == NULL;
269
270                 struct blerg *b = blerg_open(info.author);
271
272                 if ((ret & URL_INFO_RECORD) && (ret & URL_INFO_RECORD_TO)) {
273                         response = create_response_for_range(b, info.record, info.record_to);
274                 } else if (ret & URL_INFO_RECORD) {
275                         ret = blerg_fetch(b, info.record, &data, &len);
276                         blerg_close(b);
277
278                         if (ret == 0)
279                                 return respond_404(connection);
280                         response = MHD_create_response_from_data(len, data, MHD_YES, MHD_NO);
281                 } else {
282                         uint64_t record_count, from, to;
283                         record_count = blerg_get_record_count(b);
284                         if (record_count == 0) {
285                                 blerg_close(b);
286                                 response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
287                         } else {
288                                 to = record_count - 1;
289                                 from = (record_count > 50 ? to - 49 : 0);
290                                 response = create_response_for_range(b, from, to);
291                         }
292                 }
293
294                 if (response == NULL) {
295                         blerg_close(b);
296                         return respond_JSON_Failure(connection);
297                 }
298
299                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
300                 MHD_destroy_response(response);
301                 return ret;
302         } else if (strncmp(url, "/tag", 4) == 0 && strlen(url) > 4) {
303                 if (*ptr == NULL) {
304                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
305                                 return respond_405(connection);
306
307                         *ptr = (void *) 1;
308                         return MHD_YES;
309                 }
310
311                 if (url[4] != '/')
312                         return respond_404(connection);
313
314                 ret = parse_url_info(url + 5, &info);
315                 if ((ret & URL_INFO_AUTHOR) == 0)
316                         return respond_404(connection);
317
318                 if (!tag_exists(info.author))
319                         return respond_404(connection);
320
321                 uint64_t recs = 50;
322                 struct tag *taglist = tag_list(info.author, 0, &recs, -1);
323
324                 if (recs == 0) {
325                         response = MHD_create_response_from_data(2, "[]", MHD_NO, MHD_NO);
326                 } else {
327                         response = create_tag_response(taglist, recs);
328                 }
329
330                 if (response == NULL)
331                         return respond_JSON_Failure(connection);
332
333                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
334                 MHD_destroy_response(response);
335
336                 return ret;
337         } else if (strncmp(url, "/put", 4) == 0) {
338                 struct put_state *ps = (struct put_state *) *ptr;
339                 char *username;
340                 char password[33];
341
342                 if (*ptr == NULL) {
343                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
344                                 return respond_405(connection);
345
346                         if (url[4] == '/')
347                                 return respond_404(connection);
348
349                         *ptr = (void *) 1;
350
351                         username = MHD_digest_auth_get_username(connection);
352                         if (username == NULL)
353                                 return respond_401(connection, MHD_NO);
354                         auth_get_password(username, password);
355
356                         ret = MHD_digest_auth_check(connection, REALM, username, password, 300);
357                         free(username);
358
359                         if (ret == MHD_INVALID_NONCE || ret == MHD_NO)
360                                 return respond_401(connection, (ret == MHD_INVALID_NONCE) ? MHD_YES : MHD_NO);
361
362                         struct put_state *ps = malloc(sizeof(struct put_state));
363                         ps->data = NULL;
364                         ps->data_size = 0;
365                         ps->pp = MHD_create_post_processor(connection, 16384, &POST_put_iterator, ps);
366                         *ptr = ps;
367                         return MHD_YES;
368                 }
369
370                 if (*upload_data_size) {
371                         MHD_post_process(ps->pp, upload_data, *upload_data_size);
372                         *upload_data_size = 0;
373                         return MHD_YES;
374                 }
375
376                 if (ps->data == NULL || ps->data_size == 0)
377                         return respond_JSON_Failure(connection);
378
379                 username = MHD_digest_auth_get_username(connection);
380                 struct blerg *b = blerg_open(username);
381                 if (b == NULL)
382                         return respond_JSON_Failure(connection);
383                 if (blerg_store(b, ps->data, ps->data_size) == -1) {
384                         blerg_close(b);
385                         return respond_JSON_Failure(connection);
386                 }
387                 blerg_close(b);
388
389                 MHD_destroy_post_processor(ps->pp);
390                 free(username);
391                 free(ps->data);
392                 free(ps);
393                 *ptr = NULL;
394
395                 return respond_JSON_Success(connection);
396         } else if (strncmp(url, "/info", 5) == 0) {
397                 if (*ptr == NULL) {
398                         *ptr = (void *) 1;
399
400                         if (strcmp(method, MHD_HTTP_METHOD_GET) != 0)
401                                 return respond_405(connection);
402                         return MHD_YES;
403                 }
404
405
406                 if (url[5] != '/')
407                         return respond_404(connection);
408
409                 ret = parse_url_info(url + 6, &info);
410                 if ((ret & URL_INFO_AUTHOR) == 0)
411                         return respond_404(connection);
412
413                 if (!blerg_exists(info.author))
414                         return respond_404(connection);
415
416                 *ptr == NULL;
417
418                 struct blerg *b = blerg_open(info.author);
419                 uint64_t record_count = blerg_get_record_count(b);
420                 blerg_close(b);
421
422                 char number[21];
423                 yajl_gen g = yajl_gen_alloc(&yajl_c, NULL);
424                 yajl_gen_map_open(g);
425                 yajl_gen_string(g, "record_count", 12);
426                 snprintf(number, 21, "%llu", record_count);
427                 yajl_gen_string(g, number, strlen(number));
428                 yajl_gen_map_close(g);
429
430                 const unsigned char *ybuf;
431                 yajl_gen_get_buf(g, &ybuf, &len);
432
433                 response = MHD_create_response_from_data(len, (void *)ybuf, MHD_NO, MHD_YES);
434                 ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
435                 MHD_destroy_response(response);
436
437                 yajl_gen_free(g);
438
439                 return ret;
440         } else if (strncmp(url, "/create", 8) == 0) {
441                 struct create_state *cs = (struct create_state *) *ptr;
442
443                 if (cs == NULL) {
444                         if (strcmp(method, MHD_HTTP_METHOD_POST) != 0)
445                                 return respond_405(connection);
446
447                         struct create_state *cs = malloc(sizeof(struct create_state));
448                         cs->username[0] = cs->password[0] = 0;
449                         cs->pp = MHD_create_post_processor(connection, 1024, &POST_create_iterator, cs);
450                         *ptr = cs;
451                         return MHD_YES;
452                 }
453
454                 if (*upload_data_size) {
455                         MHD_post_process(cs->pp, upload_data, *upload_data_size);
456                         *upload_data_size = 0;
457                         return MHD_YES;
458                 }
459
460                 if (cs->username[0] == 0 || cs->password[0] == 0)
461                         return respond_JSON_Failure(connection);
462
463                 if (blerg_exists(cs->username))
464                         return respond_JSON_Failure(connection);
465
466                 struct blerg *b = blerg_open(cs->username);
467                 blerg_close(b);
468                 auth_set_password(cs->username, cs->password);
469
470                 MHD_destroy_post_processor(cs->pp);
471                 free(cs);
472                 *ptr = NULL;
473
474                 return respond_JSON_Success(connection);
475         } else {
476                 return respond_404(connection);
477         }
478 }
479
480
481 int main(int argc, char *argv[]) {
482         struct MHD_Daemon *daemon;
483         fd_set rs, ws, es;
484         int max;
485
486         init_responses();
487
488         daemon = MHD_start_daemon(MHD_USE_DEBUG, 8080, NULL, NULL, &ahc_derp, NULL, MHD_OPTION_END);
489         if (daemon == NULL) {
490                 fprintf(stderr, "Could not start web server\n");
491                 return 1;
492         }
493
494         while (1) {
495                 FD_ZERO(&rs); FD_ZERO(&ws); FD_ZERO(&es);
496                 if (MHD_get_fdset(daemon, &rs, &ws, &es, &max) != MHD_YES) {
497                         fprintf(stderr, "Fatal error getting fd sets\n");
498                         break;
499                 }
500                 select(max + 1, &rs, &ws, &es, NULL);
501                 MHD_run(daemon);
502         }
503         MHD_stop_daemon(daemon);
504         return 0;
505 }