source: roaraudio/libroar/vio_proto.c @ 4708:c9d40761088a

Last change on this file since 4708:c9d40761088a was 4708:c9d40761088a, checked in by phi, 13 years ago

updated copyright statements

File size: 12.3 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    *(char**)data = self->content_type;
254    return 0;
255   break;
256 }
257
258 return roar_vio_ctl(self->next, cmd, data);
259}
260
261int     roar_vio_proto_close   (struct roar_vio_calls * vio) {
262 struct roar_vio_proto * self = vio->inst;
263
264 if ( roar_vio_close(self->next) == -1 )
265  return -1;
266
267 if ( self->content_type != NULL )
268  roar_mm_free(self->content_type);
269
270 roar_mm_free(self);
271
272 return 0;
273}
274
275static int _parse_header(struct roar_keyval * kv, char ** buf, int * aligned) {
276 char * p = *buf;
277 char   c = 0;
278
279 if ( !(*aligned) ) {
280  for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
281  p++;
282  if ( *p == '\n' )
283   p++;
284 }
285
286 kv->key = p;
287
288 for (; *p != 0 && *p != '\r' && *p != '\n' && *p != ':'; p++);
289
290 if ( *p == 0 )
291  return -1;
292
293 c = *p;
294 *p = 0;
295
296 if ( c == '\r' && *(p+1) == '\n' )
297  p++;
298
299 p++;
300
301 if ( c == '\r' || c == '\n' ) {
302  if ( *(kv->key) == '\r' || *(kv->key) == '\n' )
303   return 0;
304//  printf("Key-only\n");
305  kv->value = kv->key;
306  kv->key   = NULL;
307  *buf = p;
308  return 1;
309 }
310
311 for (; *p == ' '; p++);
312
313 if ( *p == 0 )
314  return -1;
315
316 kv->value = p;
317
318 for (; *p != 0 && *p != '\r' && *p != '\n'; p++);
319
320 if ( *p == 0 )
321  return -1;
322
323 c = *p;
324 *p = 0;
325
326 if ( c == '\r' && *(p+1) == '\n' )
327  p++;
328
329 p++;
330
331 *buf = p;
332
333 if ( c == '\r' || c == '\n' ) {
334//  printf("aligned\n");
335  *aligned = 1;
336  p++;
337 } else {
338//  printf("non-aligned(c=0x%x)\n", (int)c);
339  *aligned = 0;
340 }
341
342 if ( *(kv->key) != 0 )
343  return 1;
344
345 return 0;
346}
347
348static void _handle_header (struct roar_vio_proto * self, struct roar_keyval * kv) {
349 ROAR_DBG("_handle_header(*): Header: key='%s', value='%s'", kv->key, kv->value);
350
351 if ( kv->key == NULL || kv->value == NULL )
352  return;
353
354 if ( !strcasecmp(kv->key, "Content-Type") ) {
355  if ( self->content_type != NULL )
356   roar_mm_free(self->content_type);
357
358  self->content_type = roar_mm_strdup(kv->value);
359 }
360}
361
362int roar_vio_open_proto_http   (struct roar_vio_calls * calls, struct roar_vio_calls * dst, char * host, char * file) {
363 struct roar_keyval kv;
364 struct roar_vio_proto * self;
365 struct roar_buffer * bufbuf;
366 void * vpbuf;
367 char * buf;
368 char * endofheader = NULL;
369 char * p;
370 char b0[80], b1[80];
371 int  status;
372 int  len;
373 int  oeflen = 4;
374 int  aligned = 1;
375
376 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
377
378 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
379  return -1;
380
381 self         = calls->inst;
382 calls->write = NULL; // Disable write as we do not support this
383
384 if ( roar_buffer_new_data(&bufbuf, 1024, &vpbuf) == -1 )
385  return -1;
386
387 buf = vpbuf;
388
389 ROAR_DBG("roar_vio_open_proto_http(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
390
391 roar_vio_printf(dst, "GET /%s HTTP/1.1\r\n", file);
392 roar_vio_printf(dst, "Host: %s\r\n", host);
393 roar_vio_printf(dst, "User-Agent: roar_vio_open_proto_http() $Revision$\r\n");
394 roar_vio_printf(dst, "Connection: close\r\n");
395 roar_vio_printf(dst, "\r\n");
396
397 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
398
399 roar_vio_sync(dst);
400
401 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
402
403 if ( (len = roar_vio_read(dst, buf, 1023)) < 1 ) {
404  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
405  roar_buffer_free(bufbuf);
406  return -1;
407 }
408
409 buf[len] = 0;
410
411 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
412
413 if ( sscanf(buf, "%79s %i %79s\n", b0, &status, b1) != 3 ) {
414  ROAR_DBG("roar_vio_open_proto_http(*) = -1");
415  roar_buffer_free(bufbuf);
416  return -1;
417 }
418
419 ROAR_DBG("roar_vio_open_proto_http(*) = ?");
420
421 if ( status != 200 ) {
422  ROAR_DBG("roar_vio_open_proto_http(*) = -1 // status=%i", status);
423  roar_buffer_free(bufbuf);
424  return -1;
425 }
426
427 ROAR_DBG("roar_vio_open_proto_http(*): status=%i", status);
428// ROAR_WARN("roar_vio_open_proto_http(*): buf='%s'", buf);
429
430 endofheader = strstr(buf, "\r\n\r\n");
431 if ( endofheader == NULL ) {
432  endofheader = strstr(buf, "\n\n");
433  oeflen = 2;
434 }
435
436 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
437
438 p = buf;
439 while ( _parse_header(&kv, &p, &aligned) > 0 )
440  if ( aligned )
441   _handle_header(self, &kv);
442
443 while ( endofheader == NULL ) {
444  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
445   return -1;
446
447  buf[len] = 0;
448  endofheader = strstr(buf, "\r\n\r\n");
449  if ( endofheader == NULL ) {
450   endofheader = strstr(buf, "\n\n");
451   oeflen = 2;
452  }
453
454/* Doesn't work good.
455  while ( _parse_header(&kv, &p, &aligned) > 0 )
456   if ( aligned )
457    _handle_header(self, &kv);
458*/
459
460  ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
461 }
462
463 ROAR_DBG("roar_vio_open_proto_http(*): endofheader=%p\n", endofheader);
464 ROAR_DBG("roar_vio_open_proto_http(*): buf=%p\n", buf);
465
466 if ( (endofheader - buf) == (len - oeflen) ) {
467  roar_buffer_free(bufbuf);
468  bufbuf = NULL;
469 }
470
471 if ( bufbuf != NULL ) {
472  roar_buffer_set_offset(bufbuf, endofheader - buf + oeflen);
473  roar_buffer_set_len(bufbuf,    len - (endofheader - buf + oeflen) - 1);
474 }
475 self->reader.buffer = bufbuf;
476
477/*
478 if ( !strcmp((buf+len)-4, "\r\n\r\n") )
479  return 0;
480
481 while (*buf != '\r' && *buf != '\n') {
482  if ( (len = roar_vio_read(dst, buf, 1023)) < 1 )
483   return -1;
484 }
485*/
486
487 return 0;
488}
489
490int roar_vio_open_proto_gopher (struct roar_vio_calls * calls, struct roar_vio_calls * dst, char * host, char * file) {
491 if ( calls == NULL || dst == NULL || host == NULL || file == NULL )
492  return -1;
493
494 calls->write = NULL; // Disable write as we do not support this
495
496 ROAR_DBG("roar_vio_open_proto_gopher(calls=%p, dst=%p, host='%s', file='%s') = ?", calls, dst, host, file);
497
498 if ( file[1] == '/' )
499  file += 2;
500
501 roar_vio_printf(dst, "/%s\r\n", file);
502
503 roar_vio_sync(dst); // for encryption/compression layers
504
505 return 0;
506}
507#endif
508
509//ll
Note: See TracBrowser for help on using the repository browser.