source: roaraudio/plugins/universal/protocol-http.c @ 5730:bffff5f70163

Last change on this file since 5730:bffff5f70163 was 5730:bffff5f70163, checked in by phi, 11 years ago

support index files

File size: 28.3 KB
Line 
1//protocol-http.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2011-2012
5 *
6 *  This file is part of roard a part of RoarAudio,
7 *  a cross-platform sound system for both, home and professional use.
8 *  See README for details.
9 *
10 *  This file is free software; you can redistribute it and/or modify
11 *  it under the terms of the GNU General Public License version 3
12 *  as published by the Free Software Foundation.
13 *
14 *  RoarAudio is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this software; see the file COPYING.  If not, write to
21 *  the Free Software Foundation, 51 Franklin Street, Fifth Floor,
22 *  Boston, MA 02110-1301, USA.
23 *
24 */
25
26#include <roaraudio.h>
27#ifdef ROAR_HAVE_LIBUSTE
28#include <uste.h>
29#endif
30
31struct resource_handle;
32
33enum ctl_cmd {
34 CMD_SET_CLIENTNAME
35};
36
37enum status {
38 STATUS_WAITING_FOR_HEADERS = 0,
39 STATUS_END_OF_HEADER,
40 STATUS_BAD_HEADER,
41 STATUS_RUNNING_DATA,
42 STATUS_DONE
43};
44
45struct input_source {
46 int userdata_si;
47 void * userdata_vp;
48 ssize_t offset;
49 struct roar_vio_calls * vio;
50 struct roar_vio_calls vio_store;
51};
52
53struct http_client {
54 enum status status;
55 struct roar_buffer * input_buffer;
56 char * method;
57 char * proto;
58 char * uri;
59 char * path, * query_string;
60 void * headersstore;
61 struct roar_keyval * headers;
62 ssize_t headerslen;
63 const struct resource_handle * resource;
64 struct input_source input;
65 struct roar_dl_librarypara * pluginpara;
66 int clientid;
67};
68
69struct resource_handle {
70 const char  * uri;
71 const char  * rawdata;
72 const ssize_t rawlen;
73 const char  * content_type;
74 int (*header_send)(struct http_client * self, struct roar_buffer ** obuffer);
75 int (*body_send)(struct http_client * self, struct roar_buffer ** obuffer);
76};
77
78static struct {
79 enum {
80  HOSTTYPE_GENERIC = 0,
81  HOSTTYPE_ROARD
82 } hosttype;
83 union {
84  struct {
85   int (*clients_set_name)(int id, const char * name);
86  } roard;
87 } hostspec;
88} __host = {.hosttype = HOSTTYPE_GENERIC};
89
90static int header_send(struct roar_buffer ** obuffer, int status, const char * msg, const char * content_type, ssize_t len);
91static void send_errorpage(struct http_client * self, struct roar_buffer ** obuffer, int error, const char * msg);
92
93static int slow_zero_stream(struct http_client * self, struct roar_buffer ** obuffer) {
94 struct roar_buffer * buffer;
95 void * data;
96
97 self->status = STATUS_RUNNING_DATA;
98
99 if ( roar_buffer_new_data(&buffer, 1, &data) == -1 )
100  return -1;
101
102 *((char*)data) = 0;
103
104 if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
105  roar_buffer_free(buffer);
106  return -1;
107 }
108
109 return 0;
110}
111
112static const char * __res_vio_content_type_get(struct http_client * self) {
113 const char * point = rindex(self->path, '.');
114
115 if ( point == NULL )
116  return NULL;
117
118 point++;
119
120 if ( !strcmp(point, "txt") || !strcmp(point, "text") ) {
121  return "text/plain";
122 } else if ( !strcmp(point, "htm") || !strcmp(point, "html") ) {
123  return "text/html";
124 } else if ( !strcmp(point, "css") ) {
125  return "text/css";
126 } else if ( !strcmp(point, "png") ) {
127  return "image/png";
128 } else if ( !strcmp(point, "jpg") || !strcmp(point, "jpeg") ) {
129  return "image/jpeg";
130 }
131
132 return NULL;
133}
134
135#ifdef ROAR_HAVE_LIBUSTE
136static uste_var_t __res_vio_handle_uste_build_headers(struct http_client * self) {
137 uste_var_t root = uste_var_new_kv();
138 uste_var_t var;
139 ssize_t i;
140
141 if ( root == NULL )
142  return NULL;
143
144 uste_var_set_key(root, "header");
145
146 for (i = 0; i < self->headerslen; i++) {
147  var = uste_var_new_str(self->headers[i].value == NULL ? "" : self->headers[i].value);
148  if ( self->headers[i].key != NULL )
149   uste_var_set_key(var, self->headers[i].key);
150  uste_var_push(root, var);
151  uste_var_unref(var);
152 }
153
154 return root;
155}
156
157static inline int __is_hex(char c) {
158 if ( c >= '0' && c <= '9' )
159  return 1;
160 if ( c >= 'a' && c <= 'f' )
161  return 1;
162 if ( c >= 'A' && c <= 'F' )
163  return 1;
164 return 0;
165}
166
167static inline int __hex2num(char c) {
168 if ( c >= '0' && c <= '9' )
169  return c - '0';
170 if ( c >= 'a' && c <= 'f' )
171  return c - 'a' + 10;
172 if ( c >= 'A' && c <= 'F' )
173  return c - 'A' + 10;
174 return 0;
175}
176
177static void __res_vio_handle_uste_uridecode(char * str) {
178 const char * c = str;
179
180 for (; *c; c++, str++) {
181  if ( *c == '%' && __is_hex(c[1]) && __is_hex(c[2]) ) {
182   *str = __hex2num(c[1])*16 + __hex2num(c[2]);
183   c += 2;
184  } else if ( *c == '+' ) {
185   *str = ' ';
186  } else {
187   *str = *c;
188  }
189 }
190 *str = *c;
191}
192
193static void __res_vio_handle_uste_keyify(char * str) {
194 if ( *str == '#' )
195  *str = '_';
196 for (;*str; str++)
197  if (*str == '.')
198   *str = '_';
199}
200
201static uste_var_t __res_vio_handle_uste_build_getparams(struct http_client * self) {
202 uste_var_t root = uste_var_new_kv();
203 uste_var_t subroot;
204 uste_var_t var;
205 char * qs_buffer;
206 char * cur;
207 char * state;
208 char * key, * value;
209
210 if ( root == NULL )
211  return NULL;
212
213 uste_var_set_key(root, "get");
214 if ( self->query_string == NULL )
215  return root;
216
217 qs_buffer = roar_mm_strdup(self->query_string);
218
219 // do better handling here.
220 if ( qs_buffer == NULL )
221  return root;
222
223 cur = roar_mm_strtok_r(qs_buffer, "&", &state);
224
225 while (cur != NULL) {
226  key = cur;
227  value = strstr(cur, "=");
228  if ( value != NULL ) {
229   *value = 0;
230   value++;
231   __res_vio_handle_uste_uridecode(value);
232  }
233  __res_vio_handle_uste_uridecode(key);
234  __res_vio_handle_uste_keyify(key);
235
236  if ( value == NULL ) {
237   var = uste_var_new_undef();
238  } else {
239   var = uste_var_new_str(value);
240  }
241
242  subroot = uste_var_pull(root, key);
243  if ( subroot == NULL ) {
244   subroot = uste_var_new_array();
245   if ( subroot != NULL ) {
246    uste_var_set_key(subroot, key);
247    uste_var_push(root, subroot);
248   }
249  }
250
251  if ( subroot != NULL ) {
252   uste_var_push(subroot, var);
253   uste_var_unref(subroot);
254  }
255  uste_var_unref(var);
256  cur = roar_mm_strtok_r(NULL, "&", &state);
257 }
258
259 roar_mm_free(qs_buffer);
260 return root;
261}
262
263static uste_var_t __res_vio_handle_uste_build_rootkv(struct http_client * self) {
264 uste_var_t root = uste_var_new_kv();
265 uste_var_t subroot, subsubroot;
266 uste_var_t var;
267
268 if ( root == NULL )
269  return NULL;
270
271#define __new_member_prefix(prefix,name) \
272   if ( prefix->name != NULL ) { \
273    var = uste_var_new_str(prefix->name); \
274    uste_var_set_key(var, #name); \
275    uste_var_push(subsubroot, var); \
276    uste_var_unref(var); \
277   }
278#define __new_member(name) __new_member_prefix(self,name)
279
280 subroot = uste_var_new_kv();
281 if ( subroot != NULL ) {
282  uste_var_set_key(subroot, "__client__");
283  uste_var_push(root, subroot);
284
285  var = uste_var_new_int(self->clientid);
286  uste_var_set_key(var, "clientid");
287  uste_var_push(subroot, var);
288  uste_var_unref(var);
289
290  subsubroot = uste_var_new_kv();
291  if ( subsubroot != NULL ) {
292   uste_var_set_key(subsubroot, "http");
293   uste_var_push(subroot, subsubroot);
294
295   __new_member(method);
296   __new_member(proto);
297   __new_member(uri);
298   __new_member(path);
299   __new_member(query_string);
300
301   var = __res_vio_handle_uste_build_headers(self);
302   uste_var_push(subsubroot, var);
303   uste_var_unref(var);
304
305   var = __res_vio_handle_uste_build_getparams(self);
306   uste_var_push(subsubroot, var);
307   uste_var_unref(var);
308
309   uste_var_unref(subsubroot);
310  }
311
312  uste_var_unref(subroot);
313 }
314
315 subroot = uste_var_new_kv();
316 if ( subroot != NULL ) {
317  uste_var_set_key(subroot, "__host__");
318  uste_var_push(root, subroot);
319
320  subsubroot = subroot;
321  __new_member_prefix(self->pluginpara, appname);
322  __new_member_prefix(self->pluginpara, abiversion);
323
324  uste_var_unref(subroot);
325 }
326
327 return root;
328}
329
330static void __res_vio_handle_uste(struct http_client * self, struct roar_buffer ** obuffer, const char * content_type) {
331 struct roar_buffer * buffer = NULL;
332 uste_renderer_t renderer;
333 uste_parser_t parser;
334 uste_var_t rootkv;
335 uste_node_t root;
336 int err;
337
338
339 parser = uste_parser_new(self->input.vio);
340 err = roar_error;
341 roar_vio_close(self->input.vio);
342 if ( parser == NULL ) {
343  send_errorpage(self, obuffer, err, NULL);
344  return;
345 }
346
347 if ( uste_parser_parse(parser) == -1 ) {
348  send_errorpage(self, obuffer, roar_error, NULL);
349  return;
350 }
351
352 root = uste_parser_get_rootnode(parser);
353 err = roar_error;
354 uste_parser_unref(parser);
355
356 if ( root == NULL ) {
357  send_errorpage(self, obuffer, err, NULL);
358  return;
359 }
360
361 renderer = uste_renderer_new();
362 err = roar_error;
363 if ( renderer == NULL ) {
364  uste_node_unref(root);
365  send_errorpage(self, obuffer, err, NULL);
366  return;
367 }
368
369 uste_stdfunc_register_all(renderer);
370 uste_libroar_register_all(renderer);
371
372 rootkv = __res_vio_handle_uste_build_rootkv(self);
373 err = roar_error;
374 if ( rootkv == NULL ) {
375  uste_node_unref(root);
376  uste_renderer_unref(renderer);
377  send_errorpage(self, obuffer, err, NULL);
378  return;
379 }
380
381 uste_renderer_set_rootvar(renderer, rootkv);
382 uste_var_unref(rootkv);
383
384 buffer = uste_node_render(root, renderer);
385 err = roar_error;
386 uste_node_unref(root);
387 uste_renderer_unref(renderer);
388
389 if ( buffer == NULL ) {
390  send_errorpage(self, obuffer, err, NULL);
391  return;
392 }
393
394 header_send(obuffer, 200, NULL, content_type, -1);
395
396 if ( buffer != NULL ) {
397  if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
398   roar_buffer_free(buffer);
399   return;
400  }
401 }
402
403 self->status = STATUS_DONE;
404}
405#endif
406
407static int __res_vio_check_uste(struct http_client * self, struct roar_buffer ** obuffer, const char * content_type) {
408 const size_t len = 14;
409 struct roar_buffer * buffer;
410 void * data;
411 ssize_t ret;
412 int err;
413
414 // TODO: use content type for some basic filtering.
415 (void)content_type;
416
417 *obuffer = NULL;
418
419 if ( roar_buffer_new_data(&buffer, len, &data) == -1 )
420  return -1;
421
422 ret = roar_vio_read(self->input.vio, data, len);
423 err = roar_error;
424
425 if ( ret < 1 ) {
426  roar_buffer_free(buffer);
427  roar_error = err;
428  return -1;
429 }
430
431 *obuffer = buffer;
432
433 if ( ret == (ssize_t)len ) {
434  return !strncmp(data, "@@@TEMPLATE@@@", len);
435 } else {
436  if ( roar_buffer_set_len(buffer, ret) == -1 ) {
437   *obuffer = NULL;
438   roar_buffer_free(buffer);
439   return -1;
440  }
441  // is this really a nice idea?
442  return 0;
443 }
444}
445
446static int __res_vio_header_send(struct http_client * self, struct roar_buffer ** obuffer) {
447 static const char * index_files[] = {NULL, "index.html", "index.txt", "index"};
448 struct roar_buffer * buffer;
449 struct roar_keyval * path;
450 char filename[1024];
451 const char * slash = "/";
452 const char * content_type = NULL;
453 int uste_check;
454 size_t i;
455
456 if ( self->pluginpara == NULL ) {
457  roar_err_set(ROAR_ERROR_INVAL);
458  return -1;
459 }
460
461 path = roar_keyval_lookup(self->pluginpara->argv, "webroot", self->pluginpara->argc, 1);
462 if ( path == NULL ) {
463  if ( roar_error == ROAR_ERROR_NOENT )
464   roar_err_set(ROAR_ERROR_INVAL);
465  return -1;
466 }
467
468 if ( path->value == NULL ) {
469  roar_err_set(ROAR_ERROR_INVAL);
470  return -1;
471 }
472
473 if ( strstr(self->path, "..") != NULL || strstr(self->path, "#") != NULL ) {
474  roar_err_set(ROAR_ERROR_INVAL);
475  return -1;
476 }
477
478 if ( (roar_mm_strlen(path->value) + roar_mm_strlen(self->path) + 2) > sizeof(filename) ) {
479  roar_err_set(ROAR_ERROR_NAMETOOLONG);
480  return -1;
481 }
482
483 if ( self->path[0] == '/' || path->value[strlen(path->value)-1] == '/' )
484  slash = "";
485
486 uste_check = -1;
487 for (i = 0; uste_check == -1 && i < (sizeof(index_files)/sizeof(*index_files)); i++) {
488  snprintf(filename, sizeof(filename), "%s%s%s%s%s", path->value, slash, self->path,
489           index_files[i] == NULL ? "" : "/",
490           index_files[i] == NULL ? "" : index_files[i]);
491
492
493  self->input.vio = &(self->input.vio_store);
494  if ( roar_vio_open_dstr_simple(self->input.vio, filename, ROAR_VIOF_READ|ROAR_VIOF_NONBLOCK) == -1 ) {
495   continue;
496  }
497
498  if ( roar_vio_ctl(self->input.vio, ROAR_VIO_CTL_GET_MIMETYPE, &content_type) == -1 )
499   content_type = NULL;
500
501  if ( content_type == NULL )
502   content_type = __res_vio_content_type_get(self);
503
504  uste_check = __res_vio_check_uste(self, &buffer, content_type);
505  if ( uste_check == -1 )
506   roar_vio_unref(self->input.vio);
507 }
508
509 if ( uste_check == -1 )
510  return -1;
511
512 if ( uste_check == 1 ) {
513  if ( buffer != NULL )
514   roar_buffer_free(buffer);
515#ifdef ROAR_HAVE_LIBUSTE
516  __res_vio_handle_uste(self, obuffer, content_type);
517  return 0;
518#else
519  roar_vio_close(self->input.vio);
520  send_errorpage(self, obuffer, ROAR_ERROR_NOSYS, NULL);
521  return 0;
522#endif
523 }
524
525 header_send(obuffer, 200, NULL, content_type, -1);
526
527 if ( buffer != NULL ) {
528  if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
529   roar_buffer_free(buffer);
530   return -1;
531  }
532 }
533
534 self->status = STATUS_RUNNING_DATA;
535 return 0;
536}
537
538static int __res_vio_body_send(struct http_client * self, struct roar_buffer ** obuffer) {
539 const size_t len = 1024;
540 struct roar_buffer * buffer;
541 void * data;
542 ssize_t ret;
543
544 if ( self->status == STATUS_DONE )
545  return 0;
546
547 if ( roar_buffer_new_data(&buffer, len, &data) == -1 )
548  return -1;
549
550 ret = roar_vio_read(self->input.vio, data, len);
551
552 if ( ret == -1 && roar_error == ROAR_ERROR_AGAIN ) {
553  ret = 0;
554 } else if ( ret < 1 ) {
555  roar_buffer_free(buffer);
556  if ( roar_error != ROAR_ERROR_AGAIN ) {
557   self->status = STATUS_DONE;
558   roar_vio_close(self->input.vio);
559  }
560  return 0;
561 }
562
563 if ( roar_buffer_set_len(buffer, ret) == -1 ) {
564  roar_buffer_free(buffer);
565  return -1;
566 }
567
568 if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
569  roar_buffer_free(buffer);
570  return -1;
571 }
572
573 return 0;
574}
575
576static const struct resource_handle _g_resources[] = {
577 {
578  .uri = "/test/*",
579  .rawdata = "Hello world!\n",
580  .rawlen  = 13,
581  .content_type = NULL,
582  .header_send = NULL,
583  .body_send = NULL
584 },
585 {
586  .uri = "/szs/*",
587  .rawdata = NULL,
588  .rawlen  = -1,
589  .content_type = NULL,
590  .header_send = NULL,
591  .body_send = slow_zero_stream
592 },
593 {
594  .uri = "*",
595  .rawdata = NULL,
596  .rawlen  = -1,
597  .content_type = NULL,
598  .header_send = __res_vio_header_send,
599  .body_send = __res_vio_body_send
600 }
601};
602
603
604static inline int __ret(struct http_client * self, struct roar_buffer ** obuffer) {
605 ROAR_DBG("__ret(self=%p, obuffer=%p{%p}): self->status=%i", self, obuffer, *obuffer, (int)self->status);
606 if ( *obuffer != NULL )
607  return 0;
608 if ( self->status == STATUS_DONE )
609  return -1;
610 return 0;
611}
612
613static int __ctl(struct http_client * self, enum ctl_cmd cmd, void * argp) {
614 switch (cmd) {
615  case CMD_SET_CLIENTNAME:
616    if ( argp == NULL ) {
617     roar_err_set(ROAR_ERROR_FAULT);
618     return -1;
619    }
620    ROAR_DBG("__ctl(self=%p, cmd=%i, argp='%s') = ?", self, (int)cmd, (const char *)argp);
621    switch (__host.hosttype) {
622     case HOSTTYPE_GENERIC:
623       roar_err_set(ROAR_ERROR_BADHOST);
624       return -1;
625      break;
626     case HOSTTYPE_ROARD:
627       __host.hostspec.roard.clients_set_name(self->clientid, argp);
628      break;
629    }
630   break;
631 }
632
633 roar_err_set(ROAR_ERROR_BADRQC);
634 return -1;
635}
636
637static void __init(struct roar_dl_librarypara * para) {
638 // TODO add some init code here.
639
640 if ( para == NULL )
641  return;
642
643 if ( !roar_dl_para_check_version(para, "roard <0/RoarAudio>", "1.0beta8") ) {
644  __host.hosttype = HOSTTYPE_ROARD;
645  __host.hostspec.roard.clients_set_name = roar_dl_getsym(ROAR_DL_HANDLE_APPLICATION, "clients_set_name", -1);
646
647  // check if *all* function have been found:
648  if ( __host.hostspec.roard.clients_set_name == NULL ) {
649   __host.hosttype = HOSTTYPE_GENERIC;
650  }
651 }
652}
653
654// this function is a wrapper for __init() with the code which should be inlined.
655static inline void _init(struct roar_dl_librarypara * para) {
656 static int inited = 0;
657 if (inited) return;
658 __init(para);
659 inited = 1;
660}
661
662static const struct resource_handle * resource_lookup_by_uri(const char * uri) {
663 size_t i;
664
665 for (i = 0; i < (sizeof(_g_resources)/sizeof(*_g_resources)); i++) {
666  if ( !roar_mm_strselcmp(_g_resources[i].uri, uri) ) {
667   return &(_g_resources[i]);
668  }
669 }
670
671 roar_err_set(ROAR_ERROR_NOENT);
672 return NULL;
673}
674
675static int resource_header_send(struct http_client * self, struct roar_buffer ** obuffer) {
676 if ( self->resource->header_send != NULL )
677  return self->resource->header_send(self, obuffer);
678
679 return header_send(obuffer, 200, NULL, self->resource->content_type, self->resource->rawlen);
680}
681
682static int resource_body_send(struct http_client * self, struct roar_buffer ** obuffer) {
683 struct roar_buffer * buffer;
684 void * data;
685 int ret;
686
687 if ( self->resource->body_send != NULL )
688  return self->resource->body_send(self, obuffer);
689
690 if ( self->resource->rawlen == -1 ) {
691  self->status = STATUS_DONE;
692  roar_err_set(ROAR_ERROR_INVAL);
693  return -1;
694 }
695
696 if ( roar_buffer_new_data(&buffer, self->resource->rawlen, &data) == -1 ) {
697  self->status = STATUS_DONE;
698  return -1;
699 }
700
701 memcpy(data, self->resource->rawdata, self->resource->rawlen);
702
703 ret = roar_buffer_moveintoqueue(obuffer, &buffer);
704 self->status = STATUS_DONE;
705
706 return ret;
707}
708
709static int header_parse(struct http_client * self) {
710 void * data;
711 size_t len;
712 size_t i;
713 char * start, * end;
714 char * start_uri;
715 char * start_headers, * end_headers = NULL;
716 char * tmp;
717 struct roar_keyval * c;
718
719 if ( roar_buffer_get_datalen(self->input_buffer, &data, &len) == -1 )
720  return -1;
721
722 ROAR_DBG("header_parse(self=%p): data='%*s'", self, (int)len, (const char*)data);
723
724 if ( len < 4 )
725  return 0;
726
727 for (i = 0; i < (len - 3); i++) {
728  if ( ((const char*)data)[i] == '\r' ) {
729   ROAR_DBG("header_parse(self=%p): i=%i", self, (int)i);
730   if ( !strncmp(data + i, "\r\n\r\n", 4) ) {
731    self->status = STATUS_END_OF_HEADER;
732    end_headers = data + i;
733    break;
734   }
735  }
736 }
737
738 if ( self->status != STATUS_END_OF_HEADER ) {
739  for (i = 0; i < (len - 1); i++) {
740   if ( ((const char*)data)[i] == '\n' ) {
741    ROAR_DBG("header_parse(self=%p): i=%i", self, (int)i);
742    if ( !strncmp(data + i, "\n\n", 2) ) {
743     self->status = STATUS_END_OF_HEADER;
744     end_headers = data + i;
745     break;
746    }
747   }
748  }
749 }
750
751 if ( self->status != STATUS_END_OF_HEADER )
752  return 0;
753
754 *end_headers = 0;
755
756 end = strstr(data, " ");
757 if ( end == NULL ) {
758  self->status = STATUS_BAD_HEADER;
759  return 1;
760 }
761
762 *end = 0;
763 end++;
764 self->method = roar_mm_strdup(data);
765
766 start = end;
767
768 end = strstr(start, " ");
769 if ( end == NULL ) {
770  self->status = STATUS_BAD_HEADER;
771  return 1;
772 }
773
774 *end = 0;
775 end++;
776 self->uri = roar_mm_strdup(start);
777 start_uri = start;
778
779 start = end;
780
781 end = strstr(start, "\r");
782 if ( end == NULL )
783  end = strstr(start, "\n");
784 if ( end == NULL ) {
785  self->status = STATUS_BAD_HEADER;
786  return 1;
787 }
788
789 *end = 0;
790 end++;
791 self->proto = roar_mm_strdup(start);
792
793 start_headers = end;
794
795 start = start_uri;
796
797 end = strstr(start, "?");
798 if ( end == NULL ) {
799  self->path = roar_mm_strdup(start);
800 } else {
801  *end = 0;
802  end++;
803  self->path = roar_mm_strdup(start);
804  self->query_string = roar_mm_strdup(end);
805 }
806
807 self->headerslen = 0;
808
809 i = end_headers - start_headers + 1;
810 self->headersstore = roar_mm_memdup(start_headers, i);
811 if ( self->headersstore == NULL ) {
812  self->status = STATUS_BAD_HEADER; // TODO: FIXME: not the right error, but works. ;)
813  return -1;
814 }
815
816 start_headers = self->headersstore;
817 end_headers   = start_headers + i;
818
819 for (tmp = start_headers; tmp < end_headers;) {
820  for (; tmp < end_headers && (*tmp == '\r' || *tmp == '\n'); tmp++);
821  if ( tmp == end_headers )
822   break;
823  self->headerslen++;
824  for (; tmp < end_headers && !(*tmp == '\r' || *tmp == '\n'); tmp++);
825 }
826
827 ROAR_DBG("header_parse(self=%p): self->headerslen=%i", self, (int)self->headerslen);
828
829 self->headers = roar_mm_malloc(sizeof(struct roar_keyval)*(self->headerslen+1));
830 if ( self->headers == NULL ) {
831  self->status = STATUS_BAD_HEADER; // TODO: FIXME: not the right error, but works. ;)
832  return -1;
833 }
834 memset(self->headers, 0, sizeof(struct roar_keyval)*(self->headerslen+1));
835 c = self->headers;
836
837 for (tmp = start_headers; tmp < end_headers;) {
838  ROAR_DBG("header_parse(self=%p): tmp='%s'", self, tmp);
839  for (; tmp < end_headers && (*tmp == '\r' || *tmp == '\n'); tmp++) *tmp = '\0';
840  if ( tmp == end_headers )
841   break;
842  ROAR_DBG("header_parse(self=%p): tmp='%s'", self, tmp);
843  c->key = tmp;
844  ROAR_DBG("header_parse(self=%p): c->key='%s'", self, c->key);
845  for (; tmp < end_headers && !(*tmp == '\r' || *tmp == '\n' || *tmp == ':'); tmp++);
846  c->value = tmp;
847  for (; tmp < end_headers && !(*tmp == '\r' || *tmp == '\n'); tmp++);
848  c++;
849 }
850
851 c->key = NULL;
852 c->value = NULL;
853
854 for (i = 0; i < (size_t)self->headerslen; i++) {
855  c = &(self->headers[i]);
856  if ( c->value[0] == '\0' ) {
857   c->value = NULL;
858   continue;
859  }
860
861  c->value[0] = '\0';
862  c->value++;
863
864  for (; c->value[0] == ' '; c->value++);
865
866  if ( c->value[0] == '\0' )
867   c->value = NULL;
868 }
869
870 return 1;
871}
872
873static int header_send(struct roar_buffer ** obuffer, int status, const char * msg, const char * content_type, ssize_t len) {
874 struct roar_buffer * buffer;
875 size_t bufferlen = 1024;
876 void * data;
877 char buffer_len[64];
878
879 if ( roar_buffer_new_data(&buffer, bufferlen, &data) == -1 )
880  return -1;
881
882 if ( msg == NULL ) {
883  switch (status) {
884   case 200: msg = "OK"; break;
885   case 400: msg = "Bad Request"; break;
886   case 404: msg = "File not found"; break;
887   case 500: msg = "Internal server error"; break;
888   default:  msg = "<<<unknown status code>>>"; break;
889  }
890 }
891
892 if ( content_type == NULL )
893  content_type = "text/plain";
894
895 if ( len == (ssize_t)-1 ) {
896  buffer_len[0] = 0;
897 } else {
898  snprintf(buffer_len, sizeof(buffer_len), "Content-Length: %lu\r\n", (long unsigned int)len);
899 }
900
901/*
902Date: Sun, 29 Jul 2012 01:08:15 GMT
903Cache-Control: no-cache,no-store
904Content-Type: text/html; charset=%s
905*/
906 snprintf(data, bufferlen, "HTTP/1.0 %i %s\r\n"
907                           "Server: protocol-http (libroar plugin)\r\n"
908                           "Connection: close\r\n"
909                           "Content-Type: %s\r\n"
910                           "%s"
911                           "\r\n",
912                           status, msg, content_type, buffer_len
913                           );
914
915 if ( roar_buffer_set_len(buffer, roar_mm_strlen(data)) == -1 ) {
916  roar_buffer_free(buffer);
917  return -1;
918 }
919
920 if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 ) {
921  roar_buffer_free(buffer);
922  return -1;
923 }
924
925 return 0;
926}
927
928static void send_errorpage(struct http_client * self, struct roar_buffer ** obuffer, int error, const char * msg) {
929 struct roar_buffer * buffer;
930 const size_t bufferlen = 1024;
931 void * data;
932 int httperror;
933
934 if ( roar_err_convert(&httperror, ROAR_ERROR_TYPE_HTTP, error, ROAR_ERROR_TYPE_ROARAUDIO) == -1 )
935  httperror = 500;
936
937 if ( msg == NULL )
938  msg = roar_error2str(error);
939
940 // send header and mark as done early so we can just return in case some of the later calls fail.
941 header_send(obuffer, httperror, msg, "text/html", -1);
942 self->status = STATUS_DONE;
943
944 if ( roar_buffer_new_data(&buffer, bufferlen, &data) == -1 )
945  return;
946
947 snprintf(data, bufferlen, "<html>\n"
948                           " <head><title>%i - %s</title></head>\n"
949                           " <body>\n"
950                           "  <h1>%i - %s</h1><hr>\n"
951                           " </body>\n"
952                           "</html>",
953                           httperror, msg, httperror, msg);
954
955 _LIBROAR_IGNORE_RET(roar_buffer_set_len(buffer, roar_mm_strlen(data)));
956
957 if ( roar_buffer_moveintoqueue(obuffer, &buffer) == -1 )
958  roar_buffer_free(buffer);
959}
960
961static void handle_client(struct http_client * self, struct roar_buffer ** obuffer) {
962 struct roar_keyval * kv;
963
964 // meta stuff:
965 // try to set client name.
966 kv = roar_keyval_lookup(self->headers, "user-agent", self->headerslen, 0);
967 if ( kv != NULL && kv->value != NULL ) {
968  __ctl(self, CMD_SET_CLIENTNAME, kv->value);
969 }
970
971 // first try lookup including query string. if nothing is found
972 // retry search with only resource path.
973 self->resource = resource_lookup_by_uri(self->uri);
974 if ( self->resource == NULL )
975  self->resource = resource_lookup_by_uri(self->path);
976
977 if ( self->resource == NULL ) {
978  send_errorpage(self, obuffer, roar_error, NULL);
979  return;
980 }
981
982 if ( resource_header_send(self, obuffer) == -1 ) {
983  send_errorpage(self, obuffer, roar_error, NULL);
984  return;
985 }
986
987 resource_body_send(self, obuffer);
988
989 //send_errorpage(self, obuffer, ROAR_ERROR_CAUSALITY, NULL);
990// send_errorpage(self, obuffer, roar_random_uint16() % ROAR_ERROR_BADLICENSE, NULL);
991 return;
992}
993
994
995static int _set_proto(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
996 struct http_client * self = roar_mm_malloc(sizeof(struct http_client));
997
998 (void)vio, (void)protopara, (void)protoparalen;
999
1000 if ( self == NULL )
1001  return -1;
1002
1003 _init(pluginpara);
1004
1005 memset(self, 0, sizeof(*self));
1006 self->status = STATUS_WAITING_FOR_HEADERS;
1007 self->headerslen = (ssize_t)-1;
1008 self->clientid = client;
1009
1010 if ( pluginpara != NULL ) {
1011  roar_dl_para_ref(pluginpara);
1012  self->pluginpara = pluginpara;
1013 }
1014
1015 *userdata = self;
1016
1017 return __ret(self, obuffer);
1018}
1019
1020static int _unset_proto(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
1021 struct http_client * self = *userdata;
1022
1023 (void)client, (void)vio, (void)obuffer, (void)protopara, (void)protoparalen, (void)pluginpara;
1024
1025 if ( self->input_buffer != NULL )
1026  roar_buffer_free(self->input_buffer);
1027
1028 if ( self->method != NULL )
1029  roar_mm_free(self->method);
1030 if ( self->proto != NULL )
1031  roar_mm_free(self->proto);
1032 if ( self->uri != NULL )
1033  roar_mm_free(self->uri);
1034 if ( self->path != NULL )
1035  roar_mm_free(self->path);
1036 if ( self->query_string != NULL )
1037  roar_mm_free(self->query_string);
1038 if ( self->headersstore != NULL )
1039  roar_mm_free(self->headersstore);
1040 if ( self->headers != NULL )
1041  roar_mm_free(self->headers);
1042
1043 if ( self->pluginpara != NULL )
1044  roar_dl_para_unref(self->pluginpara);
1045
1046 roar_mm_free(self);
1047 *userdata = NULL;
1048
1049 return 0;
1050}
1051
1052#define _INCREMENT 256
1053static int _handle(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
1054 struct http_client * self = *userdata;
1055 void * data;
1056 size_t oldlen;
1057 ssize_t ret;
1058
1059 (void)client, (void)protopara, (void)protoparalen, (void)pluginpara;
1060
1061 if ( self->status != STATUS_WAITING_FOR_HEADERS )
1062  return __ret(self, obuffer);
1063
1064 if ( self->input_buffer == NULL ) {
1065  if ( roar_buffer_new_data(&(self->input_buffer), _INCREMENT, &data) == -1 )
1066   return -1;
1067  oldlen = 0;
1068 } else {
1069  if ( roar_buffer_get_len(self->input_buffer, &oldlen) == -1 )
1070   return -1;
1071  if ( roar_buffer_set_len(self->input_buffer, oldlen + _INCREMENT) == -1 )
1072   return -1;
1073  if ( roar_buffer_get_data(self->input_buffer, &data) == -1 )
1074   return -1;
1075 }
1076
1077 data += oldlen;
1078
1079 ret = roar_vio_read(vio, data, _INCREMENT);
1080 if ( ret == (ssize_t)-1 ) {
1081  // we can safely ignore return value here as we return error anyway.
1082  _LIBROAR_IGNORE_RET(roar_buffer_set_len(self->input_buffer, oldlen));
1083  return -1;
1084 }
1085
1086 if ( roar_buffer_set_len(self->input_buffer, oldlen + ret) == -1 ) {
1087  ROAR_WARN("_handle(*): Can not reset buffer length. BAD. Error was: %s", roar_errorstring);
1088  return -1;
1089 }
1090
1091 header_parse(self);
1092
1093 if ( self->status == STATUS_BAD_HEADER ) {
1094  header_send(obuffer, 400, NULL, NULL, -1);
1095  self->status = STATUS_DONE;
1096 } else if ( self->status == STATUS_END_OF_HEADER ) {
1097  handle_client(self, obuffer);
1098 }
1099
1100 return __ret(self, obuffer);
1101}
1102
1103// this is a dummy function only used to kill the client after all data has been flushed.
1104static int _flushed(int client, struct roar_vio_calls * vio, struct roar_buffer ** obuffer, void ** userdata, const struct roar_keyval * protopara, ssize_t protoparalen, struct roar_dl_librarypara * pluginpara) {
1105 struct http_client * self = *userdata;
1106
1107 (void)client, (void)vio, (void)protopara, (void)protoparalen, (void)pluginpara;
1108
1109 ROAR_DBG("_flushed(*) = ?");
1110
1111 if ( self->status == STATUS_RUNNING_DATA )
1112  resource_body_send(self, obuffer);
1113
1114 return __ret(self, obuffer);
1115}
1116
1117static const struct roar_dl_proto proto = {
1118 .proto = ROAR_PROTO_HTTP,
1119 .description = "Hyper Text Transfer Protocol",
1120 .flags = ROAR_DL_PROTO_FLAGS_NONE,
1121 .set_proto = _set_proto,
1122 .unset_proto = _unset_proto,
1123 .handle = _handle,
1124 .flush = NULL,
1125 .flushed = _flushed,
1126 .status = NULL
1127};
1128
1129static int __reg_proto(struct roar_dl_librarypara * para, struct roar_dl_libraryinst * lib) {
1130 (void)para, (void)lib;
1131 ROAR_DL_PLUGIN_REG_FN(ROAR_DL_PROTO_SUBTYPE, proto, ROAR_DL_PROTO_VERSION);
1132 return 0;
1133}
1134
1135ROAR_DL_PLUGIN_START(protocol_http) {
1136 ROAR_DL_PLUGIN_META_PRODUCT_NIV("protocol-http", ROAR_VID_ROARAUDIO, ROAR_VNAME_ROARAUDIO);
1137 ROAR_DL_PLUGIN_META_VERSION(ROAR_VERSION_STRING);
1138 ROAR_DL_PLUGIN_META_LICENSE_TAG(GPLv3_0);
1139 ROAR_DL_PLUGIN_META_CONTACT_FLNE("Philipp", "Schafft", "ph3-der-loewe", "lion@lion.leolix.org");
1140 ROAR_DL_PLUGIN_META_DESC("Implementation of the HTTP Protocol");
1141
1142 ROAR_DL_PLUGIN_REG(ROAR_DL_FN_PROTO, __reg_proto);
1143} ROAR_DL_PLUGIN_END
1144
1145//ll
Note: See TracBrowser for help on using the repository browser.