source: roaraudio/libroar/vio_proto.c @ 5407:edd703e264d0

Last change on this file since 5407:edd703e264d0 was 5396:e2e5f307ef8b, checked in by phi, 12 years ago

better support for ROAR_VIOF_NONBLOCK

File size: 16.6 KB
Line 
1//vio_proto.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2009-2012
5 *
6 *  This file is part of libroar 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 *  libroar 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 *  NOTE for everyone want's to change something and send patches:
25 *  read README and HACKING! There a addition information on
26 *  the license of this document you need to read before you send
27 *  any patches.
28 *
29 *  NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc
30 *  or libpulse*:
31 *  The libs libroaresd, libroararts and libroarpulse link this lib
32 *  and are therefore GPL. Because of this it may be illigal to use
33 *  them with any software that uses libesd, libartsc or libpulse*.
34 */
35
36#include "libroar.h"
37
38#ifndef ROAR_WITHOUT_VIO_PROTO
39#include <roaraudio/proto_gopher.h>
40#endif
41
42int roar_vio_proto_init_def  (struct roar_vio_defaults * def, char * dstr, int proto, struct roar_vio_defaults * odef) {
43#ifndef ROAR_WITHOUT_VIO_PROTO
44 int                        port = 0;
45 int                        ret;
46 char                     * ed;
47 char                     * tmp;
48 int                        flags = ROAR_VIOF_READWRITE;
49
50 if ( def == NULL )
51  return -1;
52
53 switch (proto) {
54  case ROAR_VIO_PROTO_P_HTTP:    port =   80; break;
55  case ROAR_VIO_PROTO_P_GOPHER:  port =   70; break;
56  case ROAR_VIO_PROTO_P_ICY:     port = 8000; break;
57  default:
58    return -1;
59 }
60
61 if ( dstr == NULL )
62  dstr = "//";
63
64 if ( odef->o_flags & ROAR_VIOF_NONBLOCK )
65  flags |= ROAR_VIOF_NONBLOCK;
66
67 if ( roar_vio_dstr_init_defaults(def, ROAR_VIO_DEF_TYPE_SOCKET, flags, 0644) == -1 )
68  return -1;
69
70 if ( roar_vio_socket_init_tcp4_def(def, "localhost", port) == -1 )
71  return -1;
72
73 if ( !strncmp(dstr, "//", 2) )
74  dstr += 2;
75
76 if ( (ed = strstr(dstr, "/")) != NULL )
77  *ed = 0;
78
79 if ( (tmp = strstr(dstr, "@")) != NULL )
80  dstr = tmp + 1;
81
82 ROAR_DBG("roar_vio_proto_init_def(*): def->o_flags=%i", def->o_flags);
83
84 ret = roar_vio_socket_init_dstr_def(def, dstr, -1, SOCK_STREAM, def);
85
86 ROAR_DBG("roar_vio_proto_init_def(*): def->o_flags=%i", def->o_flags);
87
88 if ( ed != NULL )
89  *ed = '/';
90
91 ROAR_DBG("roar_vio_proto_init_def(*): dstr='%s'", dstr);
92
93 return ret;
94#else
95 return -1;
96#endif
97}
98
99int roar_vio_open_proto      (struct roar_vio_calls * calls, struct roar_vio_calls * dst,
100                              const char * dstr, int proto, struct roar_vio_defaults * odef) {
101#ifndef ROAR_WITHOUT_VIO_PROTO
102 struct roar_userpass userpass = {.subtype = -1, .user = NULL, .pass = NULL};
103 struct roar_vio_proto * self;
104 const char * host;
105 char * tmp;
106 int    ret;
107
108 ROAR_DBG("roar_vio_open_proto(calls=%p, dst=%p, dstr='%s', proto=%i, odef=%p) = ?", calls, dst, dstr, proto, odef);
109
110 if ( calls == NULL || dst == NULL || odef == NULL )
111  return -1;
112
113 ROAR_DBG("roar_vio_open_proto(*): odef->o_flags=%i", odef->o_flags);
114 ROAR_DBG("roar_vio_open_proto(*) = ?");
115
116 if ( (self = roar_mm_malloc(sizeof(struct roar_vio_proto))) == NULL )
117  return -1;
118
119 memset(self, 0, sizeof(struct roar_vio_proto));
120
121 self->next      = dst;
122
123 calls->inst     = self;
124
125 calls->read     = roar_vio_proto_read;
126 calls->write    = roar_vio_proto_write;
127 calls->sync     = roar_vio_proto_sync;
128 calls->ctl      = roar_vio_proto_ctl;
129 calls->close    = roar_vio_proto_close;
130
131 ROAR_DBG("roar_vio_open_proto(*) = ?");
132
133 if ( dstr != NULL ) {
134  dstr += 2;
135  host  = dstr;
136
137  if ( (tmp = strstr(dstr, "/")) == NULL )
138   return -1;
139
140  *tmp++ = 0;
141  dstr   = tmp;
142
143  if ( (tmp = strstr(dstr, "#")) != NULL )
144   *tmp = 0;
145 } else {
146  ROAR_DBG("roar_vio_open_proto(*): no dstr!, odef->type=%i", odef->type);
147  if ( odef->type == ROAR_VIO_DEF_TYPE_FILE ) {
148   dstr = odef->d.file;
149   host = "localhost";
150
151   for (; *dstr == '/'; dstr++);
152
153  } else if ( odef->type == ROAR_VIO_DEF_TYPE_SOCKET ) {
154   dstr = ""; // index document
155   host = odef->d.socket.host;
156  } else {
157   return -1;
158  }
159 }
160
161 if ( (tmp = strstr(host, "@")) != NULL ) {
162  userpass.user = (char*)host;
163  *tmp = 0;
164  host = tmp + 1;
165  if ( (tmp = strstr(userpass.user, ":")) != NULL ) {
166   *tmp = 0;
167   userpass.pass = tmp + 1;
168  }
169 }
170
171 ROAR_DBG("roar_vio_open_proto(*) = ?");
172 ROAR_DBG("roar_vio_open_proto(*): proto=%i, host='%s', file='%s', userpass={.user='%s', .pass='%s'}", proto, host, dstr, userpass.user, userpass.pass);
173
174 self->proto = proto;
175
176 if ( odef->o_flags & ROAR_VIOF_NONBLOCK ) {
177  if ( roar_vio_nonblock(calls, ROAR_SOCKET_BLOCK) == -1 ) {
178   return -1;
179  }
180 }
181 switch (proto) {
182  case ROAR_VIO_PROTO_P_HTTP:
183  case ROAR_VIO_PROTO_P_ICY:
184    ret = roar_vio_open_proto_http(calls, dst, host, dstr, userpass.user != NULL ? &userpass : NULL);
185   break;
186  case ROAR_VIO_PROTO_P_GOPHER:
187    ret = roar_vio_open_proto_gopher(calls, dst, host, dstr);
188   break;
189  default:
190    ROAR_DBG("roar_vio_open_proto(*) = -1 // no matching protocol");
191    roar_err_set(ROAR_ERROR_NOTSUP);
192    ret = -1;
193   break;
194 }
195 if ( odef->o_flags & ROAR_VIOF_NONBLOCK ) {
196  if ( roar_vio_nonblock(calls, ROAR_SOCKET_NONBLOCK) == -1 ) {
197   return -1;
198  }
199 }
200
201 return ret;
202#else
203 return -1;
204#endif
205}
206
207#ifndef ROAR_WITHOUT_VIO_PROTO
208ssize_t roar_vio_proto_read    (struct roar_vio_calls * vio, void *buf, size_t count) {
209 struct roar_vio_proto * self = vio->inst;
210 ssize_t ret;
211 ssize_t have = 0;
212 size_t  len;
213
214 ROAR_DBG("roar_vio_proto_read(*): have=%lli, count=%lli", (long long int)have, (long long int)count);
215
216 if ( self->reader.buffer != NULL ) {
217  len = count;
218  if ( roar_buffer_shift_out(&(self->reader.buffer), buf, &len) == -1 ) {
219   // This is very bad.
220   return -1;
221  }
222
223  if ( len ) {
224   have   = len;
225   buf   += len;
226   count -= len;
227  }
228 }
229
230 ROAR_DBG("roar_vio_proto_read(*): have=%lli, count=%lli", (long long int)have, (long long int)count);
231
232 if ( count == 0 )
233  return have;
234
235 ROAR_DBG("roar_vio_proto_read(*): have=%lli, count=%lli", (long long int)have, (long long int)count);
236
237 if ( (ret = roar_vio_read(self->next, buf, count)) == -1 )
238  return ret;
239
240 return have + ret;
241}
242
243ssize_t roar_vio_proto_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
244 struct roar_vio_proto * self = vio->inst;
245
246 return roar_vio_write(self->next, buf, count);
247}
248
249int     roar_vio_proto_sync    (struct roar_vio_calls * vio) {
250 struct roar_vio_proto * self = vio->inst;
251
252 return roar_vio_sync(self->next);
253}
254
255int     roar_vio_proto_ctl     (struct roar_vio_calls * vio, roar_vio_ctl_t cmd, void * data) {
256 struct roar_vio_proto * self = vio->inst;
257
258 if (vio == NULL || cmd == -1)
259  return -1;
260
261 ROAR_DBG("roar_vio_proto_ctl(vio=%p, cmd=0x%.8x, data=%p) = ?", vio, cmd, data);
262
263 switch (cmd) {
264  case ROAR_VIO_CTL_GET_NAME:
265    if ( data == NULL )
266     return -1;
267
268    switch (self->proto) {
269     case ROAR_VIO_PROTO_P_HTTP:
270       *(char**)data = "http";
271      break;
272     case ROAR_VIO_PROTO_P_GOPHER:
273       *(char**)data = "gopher";
274      break;
275     case ROAR_VIO_PROTO_P_ICY:
276       *(char**)data = "icy";
277      break;
278     default:
279       *(char**)data = "proto";
280      break;
281    }
282    return 0;
283   break;
284  case ROAR_VIO_CTL_GET_NEXT:
285    *(struct roar_vio_calls **)data = self->next;
286    return 0;
287   break;
288  case ROAR_VIO_CTL_SET_NEXT:
289    self->next = *(struct roar_vio_calls **)data;
290    return 0;
291   break;
292  case ROAR_VIO_CTL_GET_MIMETYPE:
293    if ( data == NULL )
294     return -1;
295
296    if ( self->content_type == NULL )
297     return -1;
298
299    *(char**)data = self->content_type;
300    return 0;
301   break;
302  case ROAR_VIO_CTL_GET_FH:
303  case ROAR_VIO_CTL_GET_SELECT_FH:
304    if ( self->reader.buffer == NULL && self->writer.buffer == NULL )
305     return roar_vio_ctl(self->next, cmd, data);
306    return -1;
307   break;
308  case ROAR_VIO_CTL_GET_READ_FH:
309  case ROAR_VIO_CTL_GET_SELECT_READ_FH:
310    if ( self->reader.buffer == NULL )
311     return roar_vio_ctl(self->next, cmd, data);
312    return -1;
313   break;
314  case ROAR_VIO_CTL_GET_WRITE_FH:
315  case ROAR_VIO_CTL_GET_SELECT_WRITE_FH:
316    if ( self->writer.buffer == NULL )
317     return roar_vio_ctl(self->next, cmd, data);
318    return -1;
319   break;
320 }
321
322 return roar_vio_ctl(self->next, cmd, data);
323}
324
325int     roar_vio_proto_close   (struct roar_vio_calls * vio) {
326 struct roar_vio_proto * self = vio->inst;
327
328 if ( roar_vio_close(self->next) == -1 )
329  return -1;
330
331 if ( self->content_type != NULL )
332  roar_mm_free(self->content_type);
333
334 roar_mm_free(self);
335
336 return 0;
337}
338
339static int _parse_header(struct roar_keyval * kv, char ** buf, int * aligned, char * endofheader) {
340 char * p = *buf;
341 char   c = 0;
342
343 if ( !(*aligned) ) {
344  for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
345  p++;
346  if ( *p == '\n' )
347   p++;
348 }
349
350 if ( p >= endofheader )
351  return -1;
352
353/*
354 if ( *p == '\r' || *p == '\n' )
355  return 0;
356 */
357
358 kv->key = p;
359
360 for (; *p != 0 && *p != '\r' && *p != '\n' && *p != ':'; p++);
361
362 if ( *p == 0 )
363  return -1;
364
365 if ( p >= endofheader )
366  return -1;
367
368 c = *p;
369 *p = 0;
370
371 if ( c == '\r' && *(p+1) == '\n' )
372  p++;
373
374 p++;
375
376 if ( c == '\r' || c == '\n' ) {
377  if ( *(kv->key) == '\r' || *(kv->key) == '\n' )
378   return 0;
379//  printf("Key-only\n");
380  kv->value = kv->key;
381  kv->key   = NULL;
382  *buf = p;
383  return 1;
384 }
385
386 for (; *p == ' '; p++);
387
388 if ( *p == 0 )
389  return -1;
390
391 kv->value = p;
392
393 for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
394
395 if ( *p == 0 )
396  return -1;
397
398 if ( p >= endofheader )
399  return -1;
400
401 c = *p;
402 *p = 0;
403
404 if ( c == '\r' && *(p+1) == '\n' )
405  p++;
406
407 p++;
408
409 *buf = p;
410
411 if ( c == '\r' || c == '\n' ) {
412//  printf("aligned\n");
413  *aligned = 1;
414  p++;
415 } else {
416//  printf("non-aligned(c=0x%x)\n", (int)c);
417  *aligned = 0;
418 }
419
420 if ( *(kv->key) != 0 )
421  return 1;
422
423 return 0;
424}
425
426static void _handle_header (struct roar_vio_proto * self, struct roar_keyval * kv) {
427 ROAR_DBG("_handle_header(*): Header: key='%s', value='%s'", kv->key, kv->value);
428
429 if ( kv->key == NULL || kv->value == NULL )
430  return;
431
432 if ( !strcasecmp(kv->key, "Content-Type") ) {
433  if ( self->content_type != NULL )
434   roar_mm_free(self->content_type);
435
436  self->content_type = roar_mm_strdup(kv->value);
437 }
438}
439
440static inline char * _up2http_auth (struct roar_userpass * up) {
441 char * inbuf, * outbuf;
442 size_t inlen, outlen;
443 ssize_t ret;
444 //Authorization: Basic dXNlcjpwdw==
445
446 if ( up == NULL )
447  return NULL;
448
449 if ( up->subtype != -1 )
450  return NULL;
451
452 if ( up->user == NULL || up->pass == NULL )
453  return NULL;
454
455 inlen = roar_mm_strlen(up->user) + roar_mm_strlen(up->pass) + 2;
456 inbuf = roar_mm_malloc(inlen);
457 if ( inbuf == NULL )
458  return NULL;
459
460 inbuf[0] = 0;
461 roar_mm_strlcat(inbuf, up->user, inlen);
462 roar_mm_strlcat(inbuf, ":", inlen);
463 roar_mm_strlcat(inbuf, up->pass, inlen);
464
465 outlen = ((inlen * 3) / 2) + 3 /* padding... */ + 6 /* 'Basic ' */;
466 outbuf = roar_mm_malloc(outlen);
467 if ( outbuf == NULL ) {
468  roar_mm_free(inbuf);
469  return NULL;
470 }
471
472 strncpy(outbuf, "Basic ", 7);
473
474 ROAR_DBG("_up2http_auth(up=%p{.subtype=%i, .user='%s', .pass='%s'): inbuf='%s', outbuf='%s'", up, up->subtype, up->user, up->pass, inbuf, outbuf);
475 ret = roar_base64_encode(NULL, outbuf + 6, outlen - 6, inbuf, inlen - 1, NULL, 1);
476 ROAR_DBG("_up2http_auth(up=%p{.subtype=%i, .user='%s', .pass='%s'): inbuf='%s', outbuf='%s'", up, up->subtype, up->user, up->pass, inbuf, outbuf);
477
478 roar_mm_free(inbuf);
479
480 if ( ret == -1 ) {
481  roar_mm_free(outbuf);
482  return NULL;
483 }
484
485 return outbuf;
486}
487
488int roar_vio_open_proto_http   (struct roar_vio_calls * calls, struct roar_vio_calls * dst, const char * host, const char * file, struct roar_userpass * up) {
489 struct roar_keyval kv;
490 struct roar_vio_proto * self;
491 struct roar_buffer * bufbuf;
492 void * vpbuf;
493 char * authbuf;
494 char * buf;
495 char * endofheader = NULL;
496 char * p;
497 char b0[80], b1[80];
498 int  status;
499 int  len;
500 int  oeflen = 4;
501 int  aligned = 1;
502
503 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
504
505 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
506  return -1;
507
508 self         = calls->inst;
509 calls->write = NULL; // Disable write as we do not support this
510
511 if ( roar_buffer_new_data(&bufbuf, 1024, &vpbuf) == -1 )
512  return -1;
513
514 buf = vpbuf;
515
516 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
517
518 if ( roar_vio_printf(dst, "GET /%s HTTP/1.1\r\n", file) == -1 )
519  return -1;
520
521 roar_vio_printf(dst, "Host: %s\r\n", host);
522 roar_vio_printf(dst, "User-Agent: roar_vio_open_proto_http() $Revision$\r\n");
523 roar_vio_printf(dst, "Connection: close\r\n");
524 if ( up != NULL ) {
525  if ( (authbuf = _up2http_auth(up)) != NULL ) {
526   roar_vio_printf(dst, "Authorization: %s\r\n", authbuf);
527   roar_mm_free(authbuf);
528  }
529 }
530 roar_vio_printf(dst, "\r\n");
531
532 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
533
534 roar_vio_sync(dst);
535
536 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
537
538 if ( (len = roar_vio_read(dst, buf, 1023)) < 1 ) {
539  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
540  roar_buffer_free(bufbuf);
541  return -1;
542 }
543
544 ROAR_DBG("roar_vio_open_proto_http(*): got %i bytes from server.", len);
545
546 buf[len] = 0;
547
548 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
549
550 if ( sscanf(buf, "%79s %i %79s\n", b0, &status, b1) != 3 ) {
551  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
552  roar_buffer_free(bufbuf);
553  return -1;
554 }
555
556 ROAR_DBG("roar_vio_open_proto_http(*): b0='%s'", b0);
557
558 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
559
560 if ( status != 200 ) {
561  ROAR_DBG("roar_vio_open_proto_http(*) = -1 // status=%i", status);
562  roar_buffer_free(bufbuf);
563  return -1;
564 }
565
566 ROAR_DBG("roar_vio_open_proto_http(*): status=%i", status);
567// ROAR_WARN("roar_vio_open_proto_http(*): buf='%s'", buf);
568
569 endofheader = strstr(buf, "\r\n\r\n");
570 if ( endofheader == NULL ) {
571  endofheader = strstr(buf, "\n\n");
572  oeflen = 2;
573 }
574
575 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
576
577 p = buf;
578 while ( _parse_header(&kv, &p, &aligned, endofheader) > 0 )
579  if ( aligned )
580   _handle_header(self, &kv);
581
582 while ( endofheader == NULL ) {
583  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
584   return -1;
585
586  buf[len] = 0;
587  endofheader = strstr(buf, "\r\n\r\n");
588  if ( endofheader == NULL ) {
589   endofheader = strstr(buf, "\n\n");
590   oeflen = 2;
591  }
592
593/* Doesn't work good.
594  while ( _parse_header(&kv, &p, &aligned, endofheader) > 0 )
595   if ( aligned )
596    _handle_header(self, &kv);
597*/
598
599  p = buf;
600  while ( _parse_header(&kv, &p, &aligned, endofheader) > 0 )
601   if ( aligned )
602    _handle_header(self, &kv);
603
604  ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
605 }
606
607 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
608 ROAR_DBG("roar_vio_open_proto_http(*): buf=%p\n", buf);
609
610 if ( (endofheader - buf) == (len - oeflen) ) {
611  roar_buffer_free(bufbuf);
612  bufbuf = NULL;
613 }
614
615 if ( bufbuf != NULL ) {
616  if ( roar_buffer_set_offset(bufbuf, endofheader - buf + oeflen) == -1 ||
617       roar_buffer_set_len(bufbuf,    len - (endofheader - buf + oeflen) - 0 /* ??? */) == -1 ) {
618   // TODO: FIXME: handle this in a better way.
619   ROAR_ERR("roar_vio_open_proto_http(*): Can not set data area of buffer %p, VERY BAD.", bufbuf);
620  }
621 }
622 self->reader.buffer = bufbuf;
623
624/*
625 if ( !strcmp((buf+len)-4, "\r\n\r\n") )
626  return 0;
627
628 while (*buf != '\r' && *buf != '\n') {
629  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
630   return -1;
631 }
632*/
633
634 return 0;
635}
636
637int roar_vio_open_proto_gopher (struct roar_vio_calls * calls, struct roar_vio_calls * dst, const char * host, const char * file) {
638 struct roar_vio_proto * self;
639 char type;
640 const char * mime = NULL;
641
642 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
643  return -1;
644
645 self         = calls->inst;
646 calls->write = NULL; // Disable write as we do not support this
647
648 ROAR_DBG("roar_vio_open_proto_gopher(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
649
650 type = file[0];
651
652 file++;
653
654 ROAR_DBG("roar_vio_open_proto_gopher(*): type='%c'", type);
655
656 switch (type) {
657  case ROAR_GOPHER_TYPE_FILE:
658    mime = "text/plain";
659   break;
660  case ROAR_GOPHER_TYPE_DIR:
661    mime = "inode/directory";
662   break;
663  case ROAR_GOPHER_TYPE_BIN:
664    mime = "application/octet-stream";
665   break;
666  case ROAR_GOPHER_TYPE_GIF:
667    mime = "image/gif";
668   break;
669  case ROAR_GOPHER_TYPE_HTML:
670    mime = "text/html";
671   break;
672 }
673
674 if ( mime != NULL ) {
675  self->content_type = roar_mm_strdup(mime);
676 }
677
678 roar_vio_printf(dst, "%s\r\n", file);
679
680 roar_vio_sync(dst); // for encryption/compression layers
681
682 return 0;
683}
684#endif
685
686//ll
Note: See TracBrowser for help on using the repository browser.