source: roaraudio/libroar/vio_proto.c @ 4827:1c7fe40fd2d3

Last change on this file since 4827:1c7fe40fd2d3 was 4827:1c7fe40fd2d3, checked in by phi, 13 years ago

return error on error, do not throw NULL-pointers around.

File size: 12.4 KB
Line 
1//vio_proto.c:
2
3/*
4 *      Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2009-2011
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
38int roar_vio_proto_init_def  (struct roar_vio_defaults * def, char * dstr, int proto, struct roar_vio_defaults * odef) {
39#ifndef ROAR_WITHOUT_VIO_PROTO
40 int                        port = 0;
41 int                        ret;
42 char                     * ed;
43
44 if ( def == NULL )
45  return -1;
46
47 switch (proto) {
48  case ROAR_VIO_PROTO_P_HTTP:    port =   80; break;
49  case ROAR_VIO_PROTO_P_GOPHER:  port =   70; break;
50  case ROAR_VIO_PROTO_P_ICY:     port = 8000; break;
51  default:
52    return -1;
53 }
54
55 if ( dstr == NULL )
56  dstr = "//";
57
58 if ( roar_vio_dstr_init_defaults(def, ROAR_VIO_DEF_TYPE_SOCKET, O_RDWR, 0644) == -1 )
59  return -1;
60
61 if ( roar_vio_socket_init_tcp4_def(def, "localhost", port) == -1 )
62  return -1;
63
64 if ( !strncmp(dstr, "//", 2) )
65  dstr += 2;
66
67 if ( (ed = strstr(dstr, "/")) != NULL )
68  *ed = 0;
69
70 ROAR_DBG("roar_vio_proto_init_def(*): def->o_flags=%i", def->o_flags);
71
72 ret = roar_vio_socket_init_dstr_def(def, dstr, -1, SOCK_STREAM, def);
73
74 ROAR_DBG("roar_vio_proto_init_def(*): def->o_flags=%i", def->o_flags);
75
76 if ( ed != NULL )
77  *ed = '/';
78
79 ROAR_DBG("roar_vio_proto_init_def(*): dstr='%s'", dstr);
80
81 return ret;
82#else
83 return -1;
84#endif
85}
86
87int roar_vio_open_proto      (struct roar_vio_calls * calls, struct roar_vio_calls * dst,
88                              char * dstr, int proto, struct roar_vio_defaults * odef) {
89#ifndef ROAR_WITHOUT_VIO_PROTO
90 struct roar_vio_proto * self;
91 char * host;
92 char * tmp;
93
94 ROAR_DBG("roar_vio_open_proto(calls=%p, dst=%p, dstr='%s', proto=%i, odef=%p) = ?", calls, dst, dstr, proto, odef);
95
96 if ( calls == NULL || dst == NULL || odef == NULL )
97  return -1;
98
99 ROAR_DBG("roar_vio_open_proto(*): odef->o_flags=%i", odef->o_flags);
100 ROAR_DBG("roar_vio_open_proto(*) = ?");
101
102 if ( (self = roar_mm_malloc(sizeof(struct roar_vio_proto))) == NULL )
103  return -1;
104
105 memset(self, 0, sizeof(struct roar_vio_proto));
106
107 self->next      = dst;
108
109 calls->inst     = self;
110
111 calls->read     = roar_vio_proto_read;
112 calls->write    = roar_vio_proto_write;
113// calls->lseek    = roar_vio_proto_lseek; // TODO: this is currently not supported
114 calls->nonblock = roar_vio_proto_nonblock;
115 calls->sync     = roar_vio_proto_sync;
116 calls->ctl      = roar_vio_proto_ctl;
117 calls->close    = roar_vio_proto_close;
118
119 ROAR_DBG("roar_vio_open_proto(*) = ?");
120
121 if ( dstr != NULL ) {
122  dstr += 2;
123  host  = dstr;
124
125  if ( (tmp = strstr(dstr, "/")) == NULL )
126   return -1;
127
128  *tmp++ = 0;
129  dstr   = tmp;
130
131  if ( (tmp = strstr(dstr, "#")) != NULL )
132   *tmp = 0;
133 } else {
134  ROAR_DBG("roar_vio_open_proto(*): no dstr!, odef->type=%i", odef->type);
135  if ( odef->type == ROAR_VIO_DEF_TYPE_FILE ) {
136   dstr = odef->d.file;
137   host = "localhost";
138
139   for (; *dstr == '/'; dstr++);
140
141  } else if ( odef->type == ROAR_VIO_DEF_TYPE_SOCKET ) {
142   dstr = ""; // index document
143   host = odef->d.socket.host;
144  } else {
145   return -1;
146  }
147 }
148
149 ROAR_DBG("roar_vio_open_proto(*) = ?");
150 ROAR_DBG("roar_vio_open_proto(*): proto=%i, host='%s', file='%s'", proto, host, dstr);
151
152 switch (proto) {
153  case ROAR_VIO_PROTO_P_HTTP:
154  case ROAR_VIO_PROTO_P_ICY:
155    return roar_vio_open_proto_http(calls, dst, host, dstr);
156   break;
157  case ROAR_VIO_PROTO_P_GOPHER:
158    return roar_vio_open_proto_gopher(calls, dst, host, dstr);
159   break;
160 }
161
162 ROAR_DBG("roar_vio_open_proto(*) = -1 // no matching protocol");
163 return -1;
164#else
165 return -1;
166#endif
167}
168
169#ifndef ROAR_WITHOUT_VIO_PROTO
170ssize_t roar_vio_proto_read    (struct roar_vio_calls * vio, void *buf, size_t count) {
171 struct roar_vio_proto * self = vio->inst;
172 ssize_t ret;
173 ssize_t have = 0;
174 size_t  len;
175
176 if ( self->reader.buffer != NULL ) {
177  len = count;
178  if ( roar_buffer_shift_out(&(self->reader.buffer), buf, &len) == -1 ) {
179   // This is very bad.
180   return -1;
181  }
182
183  if ( len ) {
184   have   = len;
185   buf   += len;
186   count -= len;
187  }
188 }
189
190 if ( count == 0 )
191  return have;
192
193 ROAR_DBG("roar_vio_proto_read(*): have=%lli, count=%lli", (long long int)have, (long long int)count);
194
195 if ( (ret = roar_vio_read(self->next, buf, count)) == -1 )
196  return ret;
197
198 return have + ret;
199}
200
201ssize_t roar_vio_proto_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
202 struct roar_vio_proto * self = vio->inst;
203
204 return roar_vio_write(self->next, buf, count);
205}
206
207// TODO: this is currently not implemented as this is hard to implement with buffers:
208off_t   roar_vio_proto_lseek   (struct roar_vio_calls * vio, off_t offset, int whence);
209
210int     roar_vio_proto_nonblock(struct roar_vio_calls * vio, int state) {
211 struct roar_vio_proto * self = vio->inst;
212
213 /* we can simply use the next layer's nonblock as all we do in addtion *
214  * to call there functions are our buffers which do not block normaly  */
215
216 return roar_vio_nonblock(self->next, state);
217}
218
219int     roar_vio_proto_sync    (struct roar_vio_calls * vio) {
220 struct roar_vio_proto * self = vio->inst;
221
222 return roar_vio_sync(self->next);
223}
224
225int     roar_vio_proto_ctl     (struct roar_vio_calls * vio, int cmd, void * data) {
226 struct roar_vio_proto * self = vio->inst;
227
228 if (vio == NULL || cmd == -1)
229  return -1;
230
231 ROAR_DBG("roar_vio_proto_ctl(vio=%p, cmd=0x%.8x, data=%p) = ?", vio, cmd, data);
232
233 switch (cmd) {
234  case ROAR_VIO_CTL_GET_NAME:
235    if ( data == NULL )
236     return -1;
237
238    *(char**)data = "proto";
239    return 0;
240   break;
241  case ROAR_VIO_CTL_GET_NEXT:
242    *(struct roar_vio_calls **)data = self->next;
243    return 0;
244   break;
245  case ROAR_VIO_CTL_SET_NEXT:
246    self->next = *(struct roar_vio_calls **)data;
247    return 0;
248   break;
249  case ROAR_VIO_CTL_GET_MIMETYPE:
250    if ( data == NULL )
251     return -1;
252
253    if ( self->content_type == NULL )
254     return -1;
255
256    *(char**)data = self->content_type;
257    return 0;
258   break;
259 }
260
261 return roar_vio_ctl(self->next, cmd, data);
262}
263
264int     roar_vio_proto_close   (struct roar_vio_calls * vio) {
265 struct roar_vio_proto * self = vio->inst;
266
267 if ( roar_vio_close(self->next) == -1 )
268  return -1;
269
270 if ( self->content_type != NULL )
271  roar_mm_free(self->content_type);
272
273 roar_mm_free(self);
274
275 return 0;
276}
277
278static int _parse_header(struct roar_keyval * kv, char ** buf, int * aligned) {
279 char * p = *buf;
280 char   c = 0;
281
282 if ( !(*aligned) ) {
283  for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
284  p++;
285  if ( *p == '\n' )
286   p++;
287 }
288
289 kv->key = p;
290
291 for (; *p != 0 && *p != '\r' && *p != '\n' && *p != ':'; p++);
292
293 if ( *p == 0 )
294  return -1;
295
296 c = *p;
297 *p = 0;
298
299 if ( c == '\r' && *(p+1) == '\n' )
300  p++;
301
302 p++;
303
304 if ( c == '\r' || c == '\n' ) {
305  if ( *(kv->key) == '\r' || *(kv->key) == '\n' )
306   return 0;
307//  printf("Key-only\n");
308  kv->value = kv->key;
309  kv->key   = NULL;
310  *buf = p;
311  return 1;
312 }
313
314 for (; *p == ' '; p++);
315
316 if ( *p == 0 )
317  return -1;
318
319 kv->value = p;
320
321 for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
322
323 if ( *p == 0 )
324  return -1;
325
326 c = *p;
327 *p = 0;
328
329 if ( c == '\r' && *(p+1) == '\n' )
330  p++;
331
332 p++;
333
334 *buf = p;
335
336 if ( c == '\r' || c == '\n' ) {
337//  printf("aligned\n");
338  *aligned = 1;
339  p++;
340 } else {
341//  printf("non-aligned(c=0x%x)\n", (int)c);
342  *aligned = 0;
343 }
344
345 if ( *(kv->key) != 0 )
346  return 1;
347
348 return 0;
349}
350
351static void _handle_header (struct roar_vio_proto * self, struct roar_keyval * kv) {
352 ROAR_DBG("_handle_header(*): Header: key='%s', value='%s'", kv->key, kv->value);
353
354 if ( kv->key == NULL || kv->value == NULL )
355  return;
356
357 if ( !strcasecmp(kv->key, "Content-Type") ) {
358  if ( self->content_type != NULL )
359   roar_mm_free(self->content_type);
360
361  self->content_type = roar_mm_strdup(kv->value);
362 }
363}
364
365int roar_vio_open_proto_http   (struct roar_vio_calls * calls, struct roar_vio_calls * dst, char * host, char * file) {
366 struct roar_keyval kv;
367 struct roar_vio_proto * self;
368 struct roar_buffer * bufbuf;
369 void * vpbuf;
370 char * buf;
371 char * endofheader = NULL;
372 char * p;
373 char b0[80], b1[80];
374 int  status;
375 int  len;
376 int  oeflen = 4;
377 int  aligned = 1;
378
379 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
380
381 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
382  return -1;
383
384 self         = calls->inst;
385 calls->write = NULL; // Disable write as we do not support this
386
387 if ( roar_buffer_new_data(&bufbuf, 1024, &vpbuf) == -1 )
388  return -1;
389
390 buf = vpbuf;
391
392 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
393
394 roar_vio_printf(dst, "GET /%s HTTP/1.1\r\n", file);
395 roar_vio_printf(dst, "Host: %s\r\n", host);
396 roar_vio_printf(dst, "User-Agent: roar_vio_open_proto_http() $Revision$\r\n");
397 roar_vio_printf(dst, "Connection: close\r\n");
398 roar_vio_printf(dst, "\r\n");
399
400 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
401
402 roar_vio_sync(dst);
403
404 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
405
406 if ( (len = roar_vio_read(dst, buf, 1023)) < 1 ) {
407  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
408  roar_buffer_free(bufbuf);
409  return -1;
410 }
411
412 buf[len] = 0;
413
414 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
415
416 if ( sscanf(buf, "%79s %i %79s\n", b0, &status, b1) != 3 ) {
417  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
418  roar_buffer_free(bufbuf);
419  return -1;
420 }
421
422 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
423
424 if ( status != 200 ) {
425  ROAR_DBG("roar_vio_open_proto_http(*) = -1 // status=%i", status);
426  roar_buffer_free(bufbuf);
427  return -1;
428 }
429
430 ROAR_DBG("roar_vio_open_proto_http(*): status=%i", status);
431// ROAR_WARN("roar_vio_open_proto_http(*): buf='%s'", buf);
432
433 endofheader = strstr(buf, "\r\n\r\n");
434 if ( endofheader == NULL ) {
435  endofheader = strstr(buf, "\n\n");
436  oeflen = 2;
437 }
438
439 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
440
441 p = buf;
442 while ( _parse_header(&kv, &p, &aligned) > 0 )
443  if ( aligned )
444   _handle_header(self, &kv);
445
446 while ( endofheader == NULL ) {
447  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
448   return -1;
449
450  buf[len] = 0;
451  endofheader = strstr(buf, "\r\n\r\n");
452  if ( endofheader == NULL ) {
453   endofheader = strstr(buf, "\n\n");
454   oeflen = 2;
455  }
456
457/* Doesn't work good.
458  while ( _parse_header(&kv, &p, &aligned) > 0 )
459   if ( aligned )
460    _handle_header(self, &kv);
461*/
462
463  ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
464 }
465
466 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
467 ROAR_DBG("roar_vio_open_proto_http(*): buf=%p\n", buf);
468
469 if ( (endofheader - buf) == (len - oeflen) ) {
470  roar_buffer_free(bufbuf);
471  bufbuf = NULL;
472 }
473
474 if ( bufbuf != NULL ) {
475  roar_buffer_set_offset(bufbuf, endofheader - buf + oeflen);
476  roar_buffer_set_len(bufbuf,    len - (endofheader - buf + oeflen) - 1);
477 }
478 self->reader.buffer = bufbuf;
479
480/*
481 if ( !strcmp((buf+len)-4, "\r\n\r\n") )
482  return 0;
483
484 while (*buf != '\r' && *buf != '\n') {
485  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
486   return -1;
487 }
488*/
489
490 return 0;
491}
492
493int roar_vio_open_proto_gopher (struct roar_vio_calls * calls, struct roar_vio_calls * dst, char * host, char * file) {
494 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
495  return -1;
496
497 calls->write = NULL; // Disable write as we do not support this
498
499 ROAR_DBG("roar_vio_open_proto_gopher(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
500
501 if ( file[1] == '/' )
502  file += 2;
503
504 roar_vio_printf(dst, "/%s\r\n", file);
505
506 roar_vio_sync(dst); // for encryption/compression layers
507
508 return 0;
509}
510#endif
511
512//ll
Note: See TracBrowser for help on using the repository browser.