source: roaraudio/plugins/universal/protocol-http.c @ 5961:06e7fd9e4c25

Last change on this file since 5961:06e7fd9e4c25 was 5961:06e7fd9e4c25, checked in by phi, 10 years ago

Updates of copyright and license headers

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