source: roaraudio/libroar/vio_proto.c @ 5438:4eb05969f66c

Last change on this file since 5438:4eb05969f66c was 5413:c4775e53e3a0, checked in by phi, 12 years ago

fixes cppcheck warnings (false-positives)

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;
257
258 if (vio == NULL || cmd == -1)
259  return -1;
260
261 self = vio->inst;
262
263 ROAR_DBG("roar_vio_proto_ctl(vio=%p, cmd=0x%.8x, data=%p) = ?", vio, cmd, data);
264
265 switch (cmd) {
266  case ROAR_VIO_CTL_GET_NAME:
267    if ( data == NULL )
268     return -1;
269
270    switch (self->proto) {
271     case ROAR_VIO_PROTO_P_HTTP:
272       *(char**)data = "http";
273      break;
274     case ROAR_VIO_PROTO_P_GOPHER:
275       *(char**)data = "gopher";
276      break;
277     case ROAR_VIO_PROTO_P_ICY:
278       *(char**)data = "icy";
279      break;
280     default:
281       *(char**)data = "proto";
282      break;
283    }
284    return 0;
285   break;
286  case ROAR_VIO_CTL_GET_NEXT:
287    *(struct roar_vio_calls **)data = self->next;
288    return 0;
289   break;
290  case ROAR_VIO_CTL_SET_NEXT:
291    self->next = *(struct roar_vio_calls **)data;
292    return 0;
293   break;
294  case ROAR_VIO_CTL_GET_MIMETYPE:
295    if ( data == NULL )
296     return -1;
297
298    if ( self->content_type == NULL )
299     return -1;
300
301    *(char**)data = self->content_type;
302    return 0;
303   break;
304  case ROAR_VIO_CTL_GET_FH:
305  case ROAR_VIO_CTL_GET_SELECT_FH:
306    if ( self->reader.buffer == NULL && self->writer.buffer == NULL )
307     return roar_vio_ctl(self->next, cmd, data);
308    return -1;
309   break;
310  case ROAR_VIO_CTL_GET_READ_FH:
311  case ROAR_VIO_CTL_GET_SELECT_READ_FH:
312    if ( self->reader.buffer == NULL )
313     return roar_vio_ctl(self->next, cmd, data);
314    return -1;
315   break;
316  case ROAR_VIO_CTL_GET_WRITE_FH:
317  case ROAR_VIO_CTL_GET_SELECT_WRITE_FH:
318    if ( self->writer.buffer == NULL )
319     return roar_vio_ctl(self->next, cmd, data);
320    return -1;
321   break;
322 }
323
324 return roar_vio_ctl(self->next, cmd, data);
325}
326
327int     roar_vio_proto_close   (struct roar_vio_calls * vio) {
328 struct roar_vio_proto * self = vio->inst;
329
330 if ( roar_vio_close(self->next) == -1 )
331  return -1;
332
333 if ( self->content_type != NULL )
334  roar_mm_free(self->content_type);
335
336 roar_mm_free(self);
337
338 return 0;
339}
340
341static int _parse_header(struct roar_keyval * kv, char ** buf, int * aligned, char * endofheader) {
342 char * p = *buf;
343 char   c = 0;
344
345 if ( !(*aligned) ) {
346  for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
347  p++;
348  if ( *p == '\n' )
349   p++;
350 }
351
352 if ( p >= endofheader )
353  return -1;
354
355/*
356 if ( *p == '\r' || *p == '\n' )
357  return 0;
358 */
359
360 kv->key = p;
361
362 for (; *p != 0 && *p != '\r' && *p != '\n' && *p != ':'; p++);
363
364 if ( *p == 0 )
365  return -1;
366
367 if ( p >= endofheader )
368  return -1;
369
370 c = *p;
371 *p = 0;
372
373 if ( c == '\r' && *(p+1) == '\n' )
374  p++;
375
376 p++;
377
378 if ( c == '\r' || c == '\n' ) {
379  if ( *(kv->key) == '\r' || *(kv->key) == '\n' )
380   return 0;
381//  printf("Key-only\n");
382  kv->value = kv->key;
383  kv->key   = NULL;
384  *buf = p;
385  return 1;
386 }
387
388 for (; *p == ' '; p++);
389
390 if ( *p == 0 )
391  return -1;
392
393 kv->value = p;
394
395 for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
396
397 if ( *p == 0 )
398  return -1;
399
400 if ( p >= endofheader )
401  return -1;
402
403 c = *p;
404 *p = 0;
405
406 if ( c == '\r' && *(p+1) == '\n' )
407  p++;
408
409 p++;
410
411 *buf = p;
412
413 if ( c == '\r' || c == '\n' ) {
414//  printf("aligned\n");
415  *aligned = 1;
416  p++;
417 } else {
418//  printf("non-aligned(c=0x%x)\n", (int)c);
419  *aligned = 0;
420 }
421
422 if ( *(kv->key) != 0 )
423  return 1;
424
425 return 0;
426}
427
428static void _handle_header (struct roar_vio_proto * self, struct roar_keyval * kv) {
429 ROAR_DBG("_handle_header(*): Header: key='%s', value='%s'", kv->key, kv->value);
430
431 if ( kv->key == NULL || kv->value == NULL )
432  return;
433
434 if ( !strcasecmp(kv->key, "Content-Type") ) {
435  if ( self->content_type != NULL )
436   roar_mm_free(self->content_type);
437
438  self->content_type = roar_mm_strdup(kv->value);
439 }
440}
441
442static inline char * _up2http_auth (struct roar_userpass * up) {
443 char * inbuf, * outbuf;
444 size_t inlen, outlen;
445 ssize_t ret;
446 //Authorization: Basic dXNlcjpwdw==
447
448 if ( up == NULL )
449  return NULL;
450
451 if ( up->subtype != -1 )
452  return NULL;
453
454 if ( up->user == NULL || up->pass == NULL )
455  return NULL;
456
457 inlen = roar_mm_strlen(up->user) + roar_mm_strlen(up->pass) + 2;
458 inbuf = roar_mm_malloc(inlen);
459 if ( inbuf == NULL )
460  return NULL;
461
462 inbuf[0] = 0;
463 roar_mm_strlcat(inbuf, up->user, inlen);
464 roar_mm_strlcat(inbuf, ":", inlen);
465 roar_mm_strlcat(inbuf, up->pass, inlen);
466
467 outlen = ((inlen * 3) / 2) + 3 /* padding... */ + 6 /* 'Basic ' */;
468 outbuf = roar_mm_malloc(outlen);
469 if ( outbuf == NULL ) {
470  roar_mm_free(inbuf);
471  return NULL;
472 }
473
474 strncpy(outbuf, "Basic ", 7);
475
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 ret = roar_base64_encode(NULL, outbuf + 6, outlen - 6, inbuf, inlen - 1, NULL, 1);
478 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);
479
480 roar_mm_free(inbuf);
481
482 if ( ret == -1 ) {
483  roar_mm_free(outbuf);
484  return NULL;
485 }
486
487 return outbuf;
488}
489
490int 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) {
491 struct roar_keyval kv;
492 struct roar_vio_proto * self;
493 struct roar_buffer * bufbuf;
494 void * vpbuf;
495 char * authbuf;
496 char * buf;
497 char * endofheader = NULL;
498 char * p;
499 char b0[80], b1[80];
500 int  status;
501 int  len;
502 int  oeflen = 4;
503 int  aligned = 1;
504
505 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
506
507 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
508  return -1;
509
510 self         = calls->inst;
511 calls->write = NULL; // Disable write as we do not support this
512
513 if ( roar_buffer_new_data(&bufbuf, 1024, &vpbuf) == -1 )
514  return -1;
515
516 buf = vpbuf;
517
518 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
519
520 if ( roar_vio_printf(dst, "GET /%s HTTP/1.1\r\n", file) == -1 )
521  return -1;
522
523 roar_vio_printf(dst, "Host: %s\r\n", host);
524 roar_vio_printf(dst, "User-Agent: roar_vio_open_proto_http() $Revision$\r\n");
525 roar_vio_printf(dst, "Connection: close\r\n");
526 if ( up != NULL ) {
527  if ( (authbuf = _up2http_auth(up)) != NULL ) {
528   roar_vio_printf(dst, "Authorization: %s\r\n", authbuf);
529   roar_mm_free(authbuf);
530  }
531 }
532 roar_vio_printf(dst, "\r\n");
533
534 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
535
536 roar_vio_sync(dst);
537
538 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
539
540 if ( (len = roar_vio_read(dst, buf, 1023)) < 1 ) {
541  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
542  roar_buffer_free(bufbuf);
543  return -1;
544 }
545
546 ROAR_DBG("roar_vio_open_proto_http(*): got %i bytes from server.", len);
547
548 buf[len] = 0;
549
550 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
551
552 if ( sscanf(buf, "%79s %i %79s\n", b0, &status, b1) != 3 ) {
553  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
554  roar_buffer_free(bufbuf);
555  return -1;
556 }
557
558 ROAR_DBG("roar_vio_open_proto_http(*): b0='%s'", b0);
559
560 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
561
562 if ( status != 200 ) {
563  ROAR_DBG("roar_vio_open_proto_http(*) = -1 // status=%i", status);
564  roar_buffer_free(bufbuf);
565  return -1;
566 }
567
568 ROAR_DBG("roar_vio_open_proto_http(*): status=%i", status);
569// ROAR_WARN("roar_vio_open_proto_http(*): buf='%s'", buf);
570
571 endofheader = strstr(buf, "\r\n\r\n");
572 if ( endofheader == NULL ) {
573  endofheader = strstr(buf, "\n\n");
574  oeflen = 2;
575 }
576
577 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
578
579 p = buf;
580 while ( _parse_header(&kv, &p, &aligned, endofheader) > 0 )
581  if ( aligned )
582   _handle_header(self, &kv);
583
584 while ( endofheader == NULL ) {
585  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
586   return -1;
587
588  buf[len] = 0;
589  endofheader = strstr(buf, "\r\n\r\n");
590  if ( endofheader == NULL ) {
591   endofheader = strstr(buf, "\n\n");
592   oeflen = 2;
593  }
594
595/* Doesn't work good.
596  while ( _parse_header(&kv, &p, &aligned, endofheader) > 0 )
597   if ( aligned )
598    _handle_header(self, &kv);
599*/
600
601  p = buf;
602  while ( _parse_header(&kv, &p, &aligned, endofheader) > 0 )
603   if ( aligned )
604    _handle_header(self, &kv);
605
606  ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
607 }
608
609 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
610 ROAR_DBG("roar_vio_open_proto_http(*): buf=%p\n", buf);
611
612 if ( (endofheader - buf) == (len - oeflen) ) {
613  roar_buffer_free(bufbuf);
614  bufbuf = NULL;
615 }
616
617 if ( bufbuf != NULL ) {
618  if ( roar_buffer_set_offset(bufbuf, endofheader - buf + oeflen) == -1 ||
619       roar_buffer_set_len(bufbuf,    len - (endofheader - buf + oeflen) - 0 /* ??? */) == -1 ) {
620   // TODO: FIXME: handle this in a better way.
621   ROAR_ERR("roar_vio_open_proto_http(*): Can not set data area of buffer %p, VERY BAD.", bufbuf);
622  }
623 }
624 self->reader.buffer = bufbuf;
625
626/*
627 if ( !strcmp((buf+len)-4, "\r\n\r\n") )
628  return 0;
629
630 while (*buf != '\r' && *buf != '\n') {
631  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
632   return -1;
633 }
634*/
635
636 return 0;
637}
638
639int roar_vio_open_proto_gopher (struct roar_vio_calls * calls, struct roar_vio_calls * dst, const char * host, const char * file) {
640 struct roar_vio_proto * self;
641 char type;
642 const char * mime = NULL;
643
644 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
645  return -1;
646
647 self         = calls->inst;
648 calls->write = NULL; // Disable write as we do not support this
649
650 ROAR_DBG("roar_vio_open_proto_gopher(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
651
652 type = file[0];
653
654 file++;
655
656 ROAR_DBG("roar_vio_open_proto_gopher(*): type='%c'", type);
657
658 switch (type) {
659  case ROAR_GOPHER_TYPE_FILE:
660    mime = "text/plain";
661   break;
662  case ROAR_GOPHER_TYPE_DIR:
663    mime = "inode/directory";
664   break;
665  case ROAR_GOPHER_TYPE_BIN:
666    mime = "application/octet-stream";
667   break;
668  case ROAR_GOPHER_TYPE_GIF:
669    mime = "image/gif";
670   break;
671  case ROAR_GOPHER_TYPE_HTML:
672    mime = "text/html";
673   break;
674 }
675
676 if ( mime != NULL ) {
677  self->content_type = roar_mm_strdup(mime);
678 }
679
680 roar_vio_printf(dst, "%s\r\n", file);
681
682 roar_vio_sync(dst); // for encryption/compression layers
683
684 return 0;
685}
686#endif
687
688//ll
Note: See TracBrowser for help on using the repository browser.