source: roaraudio/libroar/vio_proto.c @ 4828:4d92c3a9ebcd

Last change on this file since 4828:4d92c3a9ebcd was 4828:4d92c3a9ebcd, checked in by phi, 13 years ago

report protocol name as vio name, not generic "proto"

File size: 12.7 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 self->proto = proto;
153
154 switch (proto) {
155  case ROAR_VIO_PROTO_P_HTTP:
156  case ROAR_VIO_PROTO_P_ICY:
157    return roar_vio_open_proto_http(calls, dst, host, dstr);
158   break;
159  case ROAR_VIO_PROTO_P_GOPHER:
160    return roar_vio_open_proto_gopher(calls, dst, host, dstr);
161   break;
162 }
163
164 ROAR_DBG("roar_vio_open_proto(*) = -1 // no matching protocol");
165 return -1;
166#else
167 return -1;
168#endif
169}
170
171#ifndef ROAR_WITHOUT_VIO_PROTO
172ssize_t roar_vio_proto_read    (struct roar_vio_calls * vio, void *buf, size_t count) {
173 struct roar_vio_proto * self = vio->inst;
174 ssize_t ret;
175 ssize_t have = 0;
176 size_t  len;
177
178 if ( self->reader.buffer != NULL ) {
179  len = count;
180  if ( roar_buffer_shift_out(&(self->reader.buffer), buf, &len) == -1 ) {
181   // This is very bad.
182   return -1;
183  }
184
185  if ( len ) {
186   have   = len;
187   buf   += len;
188   count -= len;
189  }
190 }
191
192 if ( count == 0 )
193  return have;
194
195 ROAR_DBG("roar_vio_proto_read(*): have=%lli, count=%lli", (long long int)have, (long long int)count);
196
197 if ( (ret = roar_vio_read(self->next, buf, count)) == -1 )
198  return ret;
199
200 return have + ret;
201}
202
203ssize_t roar_vio_proto_write   (struct roar_vio_calls * vio, void *buf, size_t count) {
204 struct roar_vio_proto * self = vio->inst;
205
206 return roar_vio_write(self->next, buf, count);
207}
208
209// TODO: this is currently not implemented as this is hard to implement with buffers:
210off_t   roar_vio_proto_lseek   (struct roar_vio_calls * vio, off_t offset, int whence);
211
212int     roar_vio_proto_nonblock(struct roar_vio_calls * vio, int state) {
213 struct roar_vio_proto * self = vio->inst;
214
215 /* we can simply use the next layer's nonblock as all we do in addtion *
216  * to call there functions are our buffers which do not block normaly  */
217
218 return roar_vio_nonblock(self->next, state);
219}
220
221int     roar_vio_proto_sync    (struct roar_vio_calls * vio) {
222 struct roar_vio_proto * self = vio->inst;
223
224 return roar_vio_sync(self->next);
225}
226
227int     roar_vio_proto_ctl     (struct roar_vio_calls * vio, int cmd, void * data) {
228 struct roar_vio_proto * self = vio->inst;
229
230 if (vio == NULL || cmd == -1)
231  return -1;
232
233 ROAR_DBG("roar_vio_proto_ctl(vio=%p, cmd=0x%.8x, data=%p) = ?", vio, cmd, data);
234
235 switch (cmd) {
236  case ROAR_VIO_CTL_GET_NAME:
237    if ( data == NULL )
238     return -1;
239
240    switch (self->proto) {
241     case ROAR_VIO_PROTO_P_HTTP:
242       *(char**)data = "http";
243      break;
244     case ROAR_VIO_PROTO_P_GOPHER:
245       *(char**)data = "gopher";
246      break;
247     case ROAR_VIO_PROTO_P_ICY:
248       *(char**)data = "icy";
249      break;
250     default:
251       *(char**)data = "proto";
252      break;
253    }
254    return 0;
255   break;
256  case ROAR_VIO_CTL_GET_NEXT:
257    *(struct roar_vio_calls **)data = self->next;
258    return 0;
259   break;
260  case ROAR_VIO_CTL_SET_NEXT:
261    self->next = *(struct roar_vio_calls **)data;
262    return 0;
263   break;
264  case ROAR_VIO_CTL_GET_MIMETYPE:
265    if ( data == NULL )
266     return -1;
267
268    if ( self->content_type == NULL )
269     return -1;
270
271    *(char**)data = self->content_type;
272    return 0;
273   break;
274 }
275
276 return roar_vio_ctl(self->next, cmd, data);
277}
278
279int     roar_vio_proto_close   (struct roar_vio_calls * vio) {
280 struct roar_vio_proto * self = vio->inst;
281
282 if ( roar_vio_close(self->next) == -1 )
283  return -1;
284
285 if ( self->content_type != NULL )
286  roar_mm_free(self->content_type);
287
288 roar_mm_free(self);
289
290 return 0;
291}
292
293static int _parse_header(struct roar_keyval * kv, char ** buf, int * aligned) {
294 char * p = *buf;
295 char   c = 0;
296
297 if ( !(*aligned) ) {
298  for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
299  p++;
300  if ( *p == '\n' )
301   p++;
302 }
303
304 kv->key = p;
305
306 for (; *p != 0 && *p != '\r' && *p != '\n' && *p != ':'; p++);
307
308 if ( *p == 0 )
309  return -1;
310
311 c = *p;
312 *p = 0;
313
314 if ( c == '\r' && *(p+1) == '\n' )
315  p++;
316
317 p++;
318
319 if ( c == '\r' || c == '\n' ) {
320  if ( *(kv->key) == '\r' || *(kv->key) == '\n' )
321   return 0;
322//  printf("Key-only\n");
323  kv->value = kv->key;
324  kv->key   = NULL;
325  *buf = p;
326  return 1;
327 }
328
329 for (; *p == ' '; p++);
330
331 if ( *p == 0 )
332  return -1;
333
334 kv->value = p;
335
336 for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
337
338 if ( *p == 0 )
339  return -1;
340
341 c = *p;
342 *p = 0;
343
344 if ( c == '\r' && *(p+1) == '\n' )
345  p++;
346
347 p++;
348
349 *buf = p;
350
351 if ( c == '\r' || c == '\n' ) {
352//  printf("aligned\n");
353  *aligned = 1;
354  p++;
355 } else {
356//  printf("non-aligned(c=0x%x)\n", (int)c);
357  *aligned = 0;
358 }
359
360 if ( *(kv->key) != 0 )
361  return 1;
362
363 return 0;
364}
365
366static void _handle_header (struct roar_vio_proto * self, struct roar_keyval * kv) {
367 ROAR_DBG("_handle_header(*): Header: key='%s', value='%s'", kv->key, kv->value);
368
369 if ( kv->key == NULL || kv->value == NULL )
370  return;
371
372 if ( !strcasecmp(kv->key, "Content-Type") ) {
373  if ( self->content_type != NULL )
374   roar_mm_free(self->content_type);
375
376  self->content_type = roar_mm_strdup(kv->value);
377 }
378}
379
380int roar_vio_open_proto_http   (struct roar_vio_calls * calls, struct roar_vio_calls * dst, char * host, char * file) {
381 struct roar_keyval kv;
382 struct roar_vio_proto * self;
383 struct roar_buffer * bufbuf;
384 void * vpbuf;
385 char * buf;
386 char * endofheader = NULL;
387 char * p;
388 char b0[80], b1[80];
389 int  status;
390 int  len;
391 int  oeflen = 4;
392 int  aligned = 1;
393
394 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
395
396 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
397  return -1;
398
399 self         = calls->inst;
400 calls->write = NULL; // Disable write as we do not support this
401
402 if ( roar_buffer_new_data(&bufbuf, 1024, &vpbuf) == -1 )
403  return -1;
404
405 buf = vpbuf;
406
407 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
408
409 roar_vio_printf(dst, "GET /%s HTTP/1.1\r\n", file);
410 roar_vio_printf(dst, "Host: %s\r\n", host);
411 roar_vio_printf(dst, "User-Agent: roar_vio_open_proto_http() $Revision$\r\n");
412 roar_vio_printf(dst, "Connection: close\r\n");
413 roar_vio_printf(dst, "\r\n");
414
415 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
416
417 roar_vio_sync(dst);
418
419 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
420
421 if ( (len = roar_vio_read(dst, buf, 1023)) < 1 ) {
422  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
423  roar_buffer_free(bufbuf);
424  return -1;
425 }
426
427 buf[len] = 0;
428
429 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
430
431 if ( sscanf(buf, "%79s %i %79s\n", b0, &status, b1) != 3 ) {
432  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
433  roar_buffer_free(bufbuf);
434  return -1;
435 }
436
437 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
438
439 if ( status != 200 ) {
440  ROAR_DBG("roar_vio_open_proto_http(*) = -1 // status=%i", status);
441  roar_buffer_free(bufbuf);
442  return -1;
443 }
444
445 ROAR_DBG("roar_vio_open_proto_http(*): status=%i", status);
446// ROAR_WARN("roar_vio_open_proto_http(*): buf='%s'", buf);
447
448 endofheader = strstr(buf, "\r\n\r\n");
449 if ( endofheader == NULL ) {
450  endofheader = strstr(buf, "\n\n");
451  oeflen = 2;
452 }
453
454 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
455
456 p = buf;
457 while ( _parse_header(&kv, &p, &aligned) > 0 )
458  if ( aligned )
459   _handle_header(self, &kv);
460
461 while ( endofheader == NULL ) {
462  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
463   return -1;
464
465  buf[len] = 0;
466  endofheader = strstr(buf, "\r\n\r\n");
467  if ( endofheader == NULL ) {
468   endofheader = strstr(buf, "\n\n");
469   oeflen = 2;
470  }
471
472/* Doesn't work good.
473  while ( _parse_header(&kv, &p, &aligned) > 0 )
474   if ( aligned )
475    _handle_header(self, &kv);
476*/
477
478  ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
479 }
480
481 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
482 ROAR_DBG("roar_vio_open_proto_http(*): buf=%p\n", buf);
483
484 if ( (endofheader - buf) == (len - oeflen) ) {
485  roar_buffer_free(bufbuf);
486  bufbuf = NULL;
487 }
488
489 if ( bufbuf != NULL ) {
490  roar_buffer_set_offset(bufbuf, endofheader - buf + oeflen);
491  roar_buffer_set_len(bufbuf,    len - (endofheader - buf + oeflen) - 1);
492 }
493 self->reader.buffer = bufbuf;
494
495/*
496 if ( !strcmp((buf+len)-4, "\r\n\r\n") )
497  return 0;
498
499 while (*buf != '\r' && *buf != '\n') {
500  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
501   return -1;
502 }
503*/
504
505 return 0;
506}
507
508int roar_vio_open_proto_gopher (struct roar_vio_calls * calls, struct roar_vio_calls * dst, char * host, char * file) {
509 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
510  return -1;
511
512 calls->write = NULL; // Disable write as we do not support this
513
514 ROAR_DBG("roar_vio_open_proto_gopher(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
515
516 if ( file[1] == '/' )
517  file += 2;
518
519 roar_vio_printf(dst, "/%s\r\n", file);
520
521 roar_vio_sync(dst); // for encryption/compression layers
522
523 return 0;
524}
525#endif
526
527//ll
Note: See TracBrowser for help on using the repository browser.