1 /*
2 * Copyright (C) 2000-2019 the xine project
3 *
4 * This file is part of xine, a free video player.
5 *
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * xine is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19 *
20 * input plugin for http network streams
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #ifdef HAVE_NETDB_H
31 #include <netdb.h>
32 #endif
33 #ifdef HAVE_SYS_SOCKET_H
34 #include <sys/socket.h>
35 #endif
36 #include <ctype.h>
37 #include <errno.h>
38 #include <zlib.h>
39
40 #ifdef WIN32
41 #include <winsock.h>
42 #endif
43
44 #define LOG_MODULE "input_http"
45 #define LOG_VERBOSE
46 /*
47 #define LOG
48 */
49
50 #include <xine/xine_internal.h>
51 #include <xine/xineutils.h>
52 #include <xine/input_plugin.h>
53 #include "tls/xine_tls.h"
54 #include "net_buf_ctrl.h"
55 #include "group_network.h"
56 #include "http_helper.h"
57 #include "input_helper.h"
58
59 #define BUFSIZE 1024
60
61 #define DEFAULT_HTTP_PORT 80
62 #define DEFAULT_HTTPS_PORT 443
63
uint64_2str(char ** s,uint64_t v)64 static inline void uint64_2str (char **s, uint64_t v) {
65 uint8_t b[44], *t = b + 21, *q = (uint8_t *)*s;
66 uint32_t u;
67 *t = 0;
68 while (v & ((uint64_t)0xffffffff << 32)) {
69 *--t = v % 10u + '0';
70 v /= 10u;
71 }
72 u = v;
73 do {
74 *--t = u % 10u + '0';
75 u /= 10u;
76 } while (u);
77 memcpy (q, t, 21);
78 *s = (char *)(q + (b + 21 - t));
79 }
80
uint32_2str(char ** s,uint32_t u)81 static inline void uint32_2str (char **s, uint32_t u) {
82 uint8_t b[24], *t = b + 12, *q = (uint8_t *)*s;
83 *t = 0;
84 do {
85 *--t = u % 10u + '0';
86 u /= 10u;
87 } while (u);
88 memcpy (q, t, 12);
89 *s = (char *)(q + (b + 12 - t));
90 }
91
str2uint64(uint8_t ** s)92 static inline uint64_t str2uint64 (uint8_t **s) {
93 uint8_t *p = *s;
94 uint64_t v = 0;
95 uint8_t z;
96 while ((z = *p ^ '0') < 10)
97 v = (v << 3) + (v << 1) + z, p++;
98 *s = p;
99 return v;
100 }
101
str2uint32(uint8_t ** s)102 static inline uint32_t str2uint32 (uint8_t **s) {
103 uint8_t *p = *s, z;
104 uint32_t v = 0;
105 while ((z = *p ^ '0') < 10)
106 v = v * 10u + z, p++;
107 *s = p;
108 return v;
109 }
110
hexstr2uint32(uint8_t ** s)111 static inline uint32_t hexstr2uint32 (uint8_t **s) {
112 static const int8_t tab_unhex[256] = {
113 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
114 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
115 -1,-1,-1,-1,-1,-2,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
116 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
117 -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
118 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
119 -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,
120 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
121 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
122 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
123 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
124 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
125 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
126 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
127 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
128 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
129 };
130 uint8_t *p = *s;
131 uint32_t v = 0;
132 int32_t z;
133 while ((z = tab_unhex[*p]) >= 0)
134 v = (v << 4) | z, p++;
135 *s = p;
136 return v;
137 }
138
139 typedef struct {
140 input_plugin_t input_plugin;
141
142 xine_stream_t *stream;
143 xine_t *xine;
144
145 nbc_t *nbc;
146
147 off_t curpos;
148 uint64_t contentlength;
149 uint64_t bytes_left;
150 uint64_t range_start;
151 uint64_t range_end;
152 uint64_t range_total;
153
154 const char *user_agent;
155
156 xine_url_t url;
157 xine_url_t proxyurl;
158
159 xine_tls_t *tls;
160
161 FILE *head_dump_file;
162
163 int use_proxy;
164 int use_tls;
165 int ret;
166 int fh;
167
168 uint32_t sgot;
169 uint32_t sdelivered;
170 uint32_t schunkleft;
171 uint32_t zgot;
172 uint32_t zdelivered;
173 #define MODE_CHUNKED 0x0001 /* content sent portion-wise */
174 #define MODE_DEFLATED 0x0002 /* content needs inflating */
175 #define MODE_HAS_TYPE 0x0004 /* there is (at least the type of) content */
176 #define MODE_HAS_LENGTH 0x0008 /* content size is known */
177 #define MODE_AGAIN 0x0010 /* follow a redirection */
178 #define MODE_INFLATING 0x0020 /* zlib inflater is up */
179 #define MODE_DONE 0x0040 /* end of content reached */
180 #define MODE_HAVE_CHUNK 0x0100 /* there are content portions left */
181 #define MODE_HAVE_SBUF 0x0200 /* there are content bytes in sbuf */
182 #define MODE_HAVE_READ 0x0400 /* socket still has data to read */
183 #define MODE_SEEKABLE 0x1000 /* server supports byte ranges */
184 #define MODE_NSV 0x2000 /* we have a nullsoft stream */
185 #define MODE_LASTFM 0x4000 /* we have a last.fm stream */
186 #define MODE_SHOUTCAST 0x8000 /* content has info inserts */
187 uint32_t mode;
188 uint32_t status;
189
190 z_stream z_state;
191
192 /* avoid annoying ui messages on fragment streams */
193 int num_msgs;
194
195 /* ShoutCast */
196 uint32_t shoutcast_interval;
197 uint32_t shoutcast_left;
198 char *shoutcast_songtitle;
199
200 char mime_type[128];
201
202 uint8_t zbuf[32 << 10];
203 uint8_t zbuf_pad[4];
204 uint8_t sbuf[32 << 10];
205 uint8_t sbuf_pad[4];
206
207 int32_t preview_size;
208 uint8_t preview[MAX_PREVIEW_SIZE];
209
210 char mrl[4096];
211 } http_input_plugin_t;
212
213 typedef struct {
214
215 input_class_t input_class;
216
217 xine_t *xine;
218
219 const char *proxyhost;
220 int proxyport;
221
222 int prot_version;
223
224 const char *proxyuser;
225 const char *proxypassword;
226 const char *noproxylist;
227
228 const char *head_dump_name;
229 } http_input_class_t;
230
sbuf_init(http_input_plugin_t * this)231 static void sbuf_init (http_input_plugin_t *this) {
232 this->zgot = 0;
233 this->zdelivered = 0;
234 this->sgot = 0;
235 this->sdelivered = 0;
236 this->schunkleft = 0;
237 this->mode &= ~(MODE_HAVE_SBUF | MODE_INFLATING);
238 }
239
sbuf_reset(http_input_plugin_t * this)240 static void sbuf_reset (http_input_plugin_t *this) {
241 this->zgot = 0;
242 this->zdelivered = 0;
243 this->sgot = 0;
244 this->sdelivered = 0;
245 this->schunkleft = 0;
246 if (this->mode & MODE_INFLATING) {
247 this->z_state.next_in = this->sbuf;
248 this->z_state.avail_in = 0;
249 this->z_state.next_out = this->sbuf;
250 this->z_state.avail_out = 0;
251 inflateEnd (&this->z_state);
252 }
253 this->mode &= ~(MODE_HAVE_SBUF | MODE_INFLATING);
254 }
255
sbuf_get_string(http_input_plugin_t * this,uint8_t ** buf)256 static int32_t sbuf_get_string (http_input_plugin_t *this, uint8_t **buf) {
257 uint8_t *p = *buf = this->sbuf + this->sdelivered;
258 while (1) {
259 /* find end of line or data */
260 {
261 uint8_t *stop = this->sbuf + this->sgot;
262 *stop = '\n';
263 while (*p != '\n')
264 p++;
265 /* found */
266 if (p != stop) {
267 size_t n = p - *buf + 1;
268 if (this->head_dump_file)
269 fwrite (*buf, 1, n, this->head_dump_file);
270 this->sdelivered += n;
271 n--;
272 if (n && (p[-1] == '\r'))
273 p--, n--;
274 *p = 0;
275 return n;
276 }
277 }
278 /* move to beginning of buffer */
279 if (this->sdelivered) {
280 size_t n = this->sgot - this->sdelivered;
281 if (n) {
282 if (this->sdelivered < n)
283 memmove (this->sbuf, this->sbuf + this->sdelivered, n);
284 else
285 memcpy (this->sbuf, this->sbuf + this->sdelivered, n);
286 }
287 *buf = p = this->sbuf;
288 p += n;
289 this->sgot = n;
290 this->sdelivered = 0;
291 }
292 {
293 int32_t r;
294 uint32_t n = sizeof (this->sbuf) - this->sgot;
295 if (n > this->bytes_left)
296 n = this->bytes_left;
297 /* buffer full or no more input */
298 if (n == 0) {
299 this->sgot = 0;
300 return -1;
301 }
302 /* refill fast buffer */
303 r = _x_tls_part_read (this->tls, p, 1, n);
304 if (r <= 0) {
305 this->mode &= ~MODE_HAVE_READ;
306 this->bytes_left = 0;
307 return -1;
308 }
309 this->sgot += r;
310 this->bytes_left -= r;
311 this->mode |= MODE_HAVE_SBUF | MODE_HAVE_READ;
312 }
313 }
314 }
315
316 /* zlib dislikes that common 1f 8b 08 00 00 00 00 00 00 00.
317 * checksum 0 _is_ wrong but also flagged unset :-/ */
sbuf_skip_gzip_head(uint8_t * buf,uint32_t len)318 static int sbuf_skip_gzip_head (uint8_t *buf, uint32_t len) {
319 uint8_t *b = buf, *stop = b + len;
320 uint32_t flags;
321
322 if (len < 10)
323 return 0;
324 if ((b[0] != 0x1f) || (b[1] != 0x8b))
325 return 0;
326 if (b[2] != 0x08)
327 return 0;
328 flags = b[3];
329 b += 10; /* timestamp, XFL, OS */
330 if (flags & 4) {
331 uint32_t len = ((uint32_t)b[1] << 8) | b[0];
332 b += 2 + len;
333 if (b > stop)
334 return 0; /* extra data */
335 }
336 buf[len] = 0;
337 if (flags & 8) {
338 while (*b++) ;
339 if (b > stop)
340 return 0; /* file name */
341 }
342 if (flags & 16) {
343 while (*b++) ;
344 if (b > stop)
345 return 0; /* comment */
346 }
347 if (flags & 2) {
348 b += 2;
349 if (b > stop)
350 return 0; /* CRC16 */
351 }
352 return b - buf;
353 }
354
sbuf_get_bytes(http_input_plugin_t * this,uint8_t * buf,size_t len)355 static ssize_t sbuf_get_bytes (http_input_plugin_t *this, uint8_t *buf, size_t len) {
356 uint8_t *q = buf;
357 size_t left = len;
358
359 switch (this->mode & (MODE_CHUNKED | MODE_DEFLATED)) {
360
361 case 0:
362 if (left == 0)
363 break;
364 /* get from fast buffer */
365 do {
366 uint32_t fast = this->sgot - this->sdelivered;
367 if (fast == 0)
368 break;
369 if (fast > left) {
370 memcpy (q, this->sbuf + this->sdelivered, left);
371 this->sdelivered += left;
372 return left;
373 }
374 memcpy (q, this->sbuf + this->sdelivered, fast);
375 q += fast;
376 left -= fast;
377 this->sgot = this->sdelivered = 0;
378 } while (0);
379 /* get the usual way */
380 if (left > this->bytes_left)
381 left = this->bytes_left;
382 if (left > 0) {
383 ssize_t r = _x_tls_read (this->tls, q, left);
384 if (r > 0) {
385 q += r;
386 this->bytes_left -= r;
387 } else {
388 this->mode &= ~MODE_HAVE_READ;
389 this->bytes_left = 0;
390 }
391 }
392 break;
393
394 case MODE_CHUNKED + MODE_DEFLATED:
395 /* sigh. chunk switches may appear in the middle of a deflate symbol.
396 * we need to remove them before presenting the stream to zlib. */
397 if (this->mode & MODE_DONE)
398 return 0;
399 while (left > 0) {
400 uint32_t have = this->zgot - this->zdelivered;
401 /* refill zbuf */
402 if ((have < 128) && (this->mode & (MODE_HAVE_CHUNK | MODE_HAVE_SBUF | MODE_HAVE_READ))) {
403 uint32_t want;
404 /* align */
405 if (this->zdelivered >= 128) {
406 if (have > 0)
407 xine_small_memcpy (this->zbuf, this->zbuf + this->zdelivered, have);
408 this->zgot = have;
409 this->zdelivered = 0;
410 }
411 /* start new chunk */
412 if ((this->schunkleft == 0) && (this->mode & MODE_HAVE_CHUNK)) {
413 uint8_t *p;
414 int32_t n = sbuf_get_string (this, &p);
415 if (n == 0)
416 n = sbuf_get_string (this, &p);
417 if (n > 0) {
418 this->schunkleft = hexstr2uint32 (&p);
419 if (this->schunkleft == 0)
420 this->mode &= ~(MODE_HAVE_CHUNK | MODE_HAVE_READ);
421 } else {
422 this->mode &= ~(MODE_HAVE_CHUNK | MODE_HAVE_READ);
423 }
424 }
425 /* refill zbuf from sbuf */
426 want = sizeof (this->zbuf) - this->zgot;
427 if (want > this->schunkleft)
428 want = this->schunkleft;
429 if (this->mode & MODE_HAVE_SBUF) {
430 uint32_t bytes = this->sgot - this->sdelivered;
431 if (bytes > want)
432 bytes = want;
433 if (bytes > 0) {
434 memcpy (this->zbuf + this->zgot, this->sbuf + this->sdelivered, bytes);
435 this->zgot += bytes;
436 this->sdelivered += bytes;
437 this->schunkleft -= bytes;
438 want -= bytes;
439 }
440 if (this->sgot <= this->sdelivered) {
441 this->sgot = 0;
442 this->sdelivered = 0;
443 this->mode &= ~MODE_HAVE_SBUF;
444 }
445 }
446 /* refill zbuf from stream */
447 if ((want > 0) && (this->mode & (MODE_HAVE_SBUF | MODE_HAVE_READ)) == MODE_HAVE_READ) {
448 int32_t r = _x_tls_read (this->tls, this->zbuf + this->zgot, want);
449 if (r > 0) {
450 this->zgot += r;
451 this->schunkleft -= r;
452 } else
453 this->mode &= ~MODE_HAVE_READ;
454 }
455 /* collect small chunks */
456 if ((this->schunkleft == 0) && (this->mode & MODE_HAVE_CHUNK))
457 continue;
458 /* new size */
459 have = this->zgot - this->zdelivered;
460 }
461 /* init zlib */
462 if ((this->mode & MODE_INFLATING) == 0) {
463 uint32_t head_len = sbuf_skip_gzip_head (this->zbuf, this->zgot);
464 this->zdelivered += head_len;
465 have -= head_len;
466 this->z_state.next_in = this->zbuf + this->zdelivered;
467 this->z_state.avail_in = have;
468 this->z_state.next_out = q;
469 this->z_state.avail_out = left;
470 this->z_state.zalloc = NULL;
471 this->z_state.zfree = NULL;
472 this->z_state.opaque = NULL;
473 if (inflateInit2 (&this->z_state, -15) != Z_OK) {
474 this->mode |= MODE_DONE;
475 break;
476 }
477 this->mode |= MODE_INFLATING;
478 }
479 /* deflate */
480 {
481 int32_t bytes;
482 int z_ret_code1;
483 this->z_state.next_in = this->zbuf + this->zdelivered;
484 this->z_state.avail_in = have;
485 this->z_state.next_out = q;
486 this->z_state.avail_out = left;
487 z_ret_code1 = inflate (&this->z_state, Z_SYNC_FLUSH);
488 if ((z_ret_code1 != Z_OK) && (z_ret_code1 != Z_STREAM_END)) {
489 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
490 "input_http: zlib error #%d.\n", z_ret_code1);
491 this->mode |= MODE_DONE;
492 break;
493 }
494 bytes = have - this->z_state.avail_in;
495 if (bytes < 0)
496 break;
497 this->zdelivered += bytes;
498 if (this->zdelivered > this->zgot) {
499 xprintf (this->xine, XINE_VERBOSITY_DEBUG, "input_http: zbuf overrun??\n");
500 this->zdelivered = this->zgot = 0;
501 }
502 bytes = left - this->z_state.avail_out;
503 if (bytes < 0)
504 break;
505 q += bytes;
506 left -= bytes;
507 if (z_ret_code1 == Z_STREAM_END) {
508 this->mode |= MODE_DONE;
509 break;
510 }
511 }
512 }
513 if ((this->mode & (MODE_DONE | MODE_INFLATING)) == (MODE_DONE | MODE_INFLATING)) {
514 int bytes_out;
515 bytes_out = this->z_state.avail_out;
516 inflateEnd (&this->z_state);
517 bytes_out -= this->z_state.avail_out;
518 if (bytes_out > 0)
519 q += bytes_out;
520 this->mode &= ~MODE_INFLATING;
521 }
522 break;
523
524 case MODE_CHUNKED:
525 if (this->mode & MODE_DONE)
526 return 0;
527 while (left > 0) {
528 uint32_t fast, chunkleft;
529 if (this->schunkleft == 0) {
530 uint8_t *p;
531 int32_t n;
532 if (this->mode & MODE_DONE)
533 return 0;
534 n = sbuf_get_string (this, &p);
535 if (n == 0)
536 n = sbuf_get_string (this, &p);
537 if (n <= 0)
538 break;
539 this->schunkleft = hexstr2uint32 (&p);
540 if (this->schunkleft == 0) {
541 this->mode |= MODE_DONE;
542 break;
543 }
544 }
545 chunkleft = left < this->schunkleft ? left : this->schunkleft;
546 fast = this->sgot - this->sdelivered;
547 if (fast > chunkleft)
548 fast = chunkleft;
549 if (fast > 0) {
550 /* get from fast buffer */
551 memcpy (q, this->sbuf + this->sdelivered, fast);
552 q += fast;
553 left -= fast;
554 this->schunkleft -= fast;
555 this->sdelivered += fast;
556 if (this->sgot == this->sdelivered)
557 this->sgot = this->sdelivered = 0;
558 } else {
559 /* get the usual way */
560 ssize_t r = _x_tls_read (this->tls, q, chunkleft);
561 if (r <= 0) {
562 this->mode |= MODE_DONE;
563 break;
564 }
565 q += r;
566 this->schunkleft -= r;
567 left -= r;
568 }
569 }
570 break;
571
572 case MODE_DEFLATED:
573 if (this->mode & MODE_DONE)
574 return 0;
575 while (left > 0) {
576 uint32_t have = this->sgot - this->sdelivered;
577 /* refill sbuf */
578 if ((have < 128) && (this->mode & MODE_HAVE_READ)) {
579 uint32_t want;
580 /* align */
581 if (this->sdelivered >= 128) {
582 if (have > 0)
583 xine_small_memcpy (this->sbuf, this->sbuf + this->sdelivered, have);
584 this->sgot = have;
585 this->sdelivered = 0;
586 }
587 /* refill */
588 want = sizeof (this->sbuf) - this->sgot;
589 if (want > this->bytes_left)
590 want = this->bytes_left;
591 if (want == 0) {
592 this->mode &= ~MODE_HAVE_READ;
593 this->bytes_left = 0;
594 } else {
595 int32_t r = _x_tls_read (this->tls, this->sbuf + this->sgot, want);
596 if (r > 0) {
597 this->sgot += r;
598 have += r;
599 this->bytes_left -= r;
600 } else {
601 this->mode &= ~MODE_HAVE_READ;
602 this->bytes_left = 0;
603 }
604 }
605 }
606 /* init zlib */
607 if (!(this->mode & MODE_INFLATING)) {
608 uint32_t head_len = sbuf_skip_gzip_head (this->sbuf + this->sdelivered, have);
609 this->sdelivered += head_len;
610 have -= head_len;
611 this->z_state.next_in = this->sbuf + this->sdelivered;
612 this->z_state.avail_in = have;
613 this->z_state.next_out = q;
614 this->z_state.avail_out = left;
615 this->z_state.zalloc = NULL;
616 this->z_state.zfree = NULL;
617 this->z_state.opaque = NULL;
618 if (inflateInit2 (&this->z_state, -15) != Z_OK) {
619 this->mode |= MODE_DONE;
620 break;
621 }
622 this->mode |= MODE_INFLATING;
623 }
624 /* deflate */
625 {
626 int z_ret_code1;
627 int32_t bytes;
628 this->z_state.next_in = this->sbuf + this->sdelivered;
629 this->z_state.avail_in = have;
630 this->z_state.next_out = q;
631 this->z_state.avail_out = left;
632 z_ret_code1 = inflate (&this->z_state, Z_SYNC_FLUSH);
633 if ((z_ret_code1 != Z_OK) && (z_ret_code1 != Z_STREAM_END)) {
634 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
635 "input_http: zlib error #%d.\n", z_ret_code1);
636 this->mode |= MODE_DONE;
637 break;
638 }
639 bytes = have - this->z_state.avail_in;
640 if (bytes < 0)
641 break;
642 this->sdelivered += bytes;
643 bytes = left - this->z_state.avail_out;
644 if (bytes < 0)
645 break;
646 q += bytes;
647 left -= bytes;
648 if (z_ret_code1 == Z_STREAM_END) {
649 this->mode |= MODE_DONE;
650 break;
651 }
652 }
653 }
654 if ((this->mode & (MODE_DONE | MODE_INFLATING)) == (MODE_DONE | MODE_INFLATING)) {
655 int bytes_out;
656 bytes_out = this->z_state.avail_out;
657 inflateEnd (&this->z_state);
658 bytes_out -= this->z_state.avail_out;
659 if (bytes_out > 0)
660 q += bytes_out;
661 this->mode &= ~MODE_INFLATING;
662 }
663 break;
664 }
665 return q - buf;
666 }
667
proxy_host_change_cb(void * this_gen,xine_cfg_entry_t * cfg)668 static void proxy_host_change_cb (void *this_gen, xine_cfg_entry_t *cfg) {
669 http_input_class_t *this = (http_input_class_t *)this_gen;
670
671 this->proxyhost = cfg->str_value;
672 }
673
proxy_port_change_cb(void * this_gen,xine_cfg_entry_t * cfg)674 static void proxy_port_change_cb(void *this_gen, xine_cfg_entry_t *cfg) {
675 http_input_class_t *this = (http_input_class_t *)this_gen;
676
677 this->proxyport = cfg->num_value;
678 }
679
proxy_user_change_cb(void * this_gen,xine_cfg_entry_t * cfg)680 static void proxy_user_change_cb(void *this_gen, xine_cfg_entry_t *cfg) {
681 http_input_class_t *this = (http_input_class_t *)this_gen;
682
683 this->proxyuser = cfg->str_value;
684 }
685
proxy_password_change_cb(void * this_gen,xine_cfg_entry_t * cfg)686 static void proxy_password_change_cb(void *this_gen, xine_cfg_entry_t *cfg) {
687 http_input_class_t *this = (http_input_class_t *)this_gen;
688
689 this->proxypassword = cfg->str_value;
690 }
691
no_proxy_list_change_cb(void * this_gen,xine_cfg_entry_t * cfg)692 static void no_proxy_list_change_cb(void *this_gen, xine_cfg_entry_t *cfg) {
693 http_input_class_t *this = (http_input_class_t *)this_gen;
694
695 this->noproxylist = cfg->str_value;
696 }
697
prot_version_change_cb(void * this_gen,xine_cfg_entry_t * cfg)698 static void prot_version_change_cb (void *this_gen, xine_cfg_entry_t *cfg) {
699 http_input_class_t *this = (http_input_class_t *)this_gen;
700
701 this->prot_version = cfg->num_value;
702 }
703
head_dump_name_change_cb(void * this_gen,xine_cfg_entry_t * cfg)704 static void head_dump_name_change_cb (void *this_gen, xine_cfg_entry_t *cfg) {
705 http_input_class_t *this = (http_input_class_t *)this_gen;
706
707 this->head_dump_name = cfg->str_value;
708 }
709
710 /*
711 * handle no-proxy list config option and returns, if use the proxy or not
712 * if error occurred, is expected using the proxy
713 */
_x_use_proxy(xine_t * xine,http_input_class_t * this,const char * host)714 static int _x_use_proxy(xine_t *xine, http_input_class_t *this, const char *host) {
715 const char *target;
716 char *no_proxy, *domain, *ptr = NULL;
717 struct hostent *info;
718 size_t i = 0, host_len, noprox_len;
719
720 /*
721 * get full host name
722 */
723 if ((info = gethostbyname(host)) == NULL) {
724 xine_log(xine, XINE_LOG_MSG,
725 _("input_http: gethostbyname(%s) failed: %s\n"), host,
726 hstrerror(h_errno));
727 return 1;
728 }
729 if (!info->h_name) return 1;
730
731 if ( info->h_addr_list[0] ) {
732 /* \177\0\0\1 is the *octal* representation of 127.0.0.1 */
733 if ( info->h_addrtype == AF_INET && !memcmp(info->h_addr_list[0], "\177\0\0\1", 4) ) {
734 lprintf("host '%s' is localhost\n", host);
735 return 0;
736 }
737 /* TODO: IPv6 check */
738 }
739
740 target = info->h_name;
741
742 host_len = strlen(target);
743 no_proxy = strdup(this->noproxylist);
744 domain = strtok_r(no_proxy, ",", &ptr);
745 while (domain) {
746 /* skip leading spaces */
747 while (isspace (*domain))
748 ++domain;
749 /* only check for matches if we've not reached the end of the token */
750 if (*domain) {
751 /* special-case domain beginning with '=' -> is a host name */
752 if (domain[0] == '=' && strcmp(target, domain + 1) == 0) {
753 lprintf("host '%s' is in no-proxy domain '%s'\n", target, domain);
754 free(no_proxy);
755 return 0;
756 }
757 noprox_len = strlen(domain);
758 /* special-case host==domain, avoiding dot checks */
759 if (host_len == noprox_len && strcmp(target, domain) == 0) {
760 lprintf("host '%s' is in no-proxy domain '%s'\n", target, domain);
761 free(no_proxy);
762 return 0;
763 }
764 /* check for host in domain, and require that (if matched) the domain
765 * name is preceded by a dot, either in the host or domain strings,
766 * e.g. "a.foo.bar" is in "foo.bar" and ".foo.bar" but not "o.bar"
767 */
768 if (host_len > noprox_len
769 && (domain[0] == '.' || target[host_len - noprox_len - 1] == '.')
770 && strcmp(target + host_len - noprox_len, domain) == 0) {
771 lprintf("host '%s' is in no-proxy domain '%s'\n", target, domain);
772 free(no_proxy);
773 return 0;
774 }
775 lprintf("host '%s' isn't in no-proxy domain '%s'\n", target, domain);
776 }
777 domain = strtok_r(NULL, ",", &ptr);
778 i++;
779 }
780 free(no_proxy);
781
782 return 1;
783 }
784
http_plugin_basicauth(const char * user,const char * password,char * dest,size_t len)785 static size_t http_plugin_basicauth (const char *user, const char *password, char* dest, size_t len) {
786 size_t s1 = strlen (user);
787 size_t s2 = password ? strlen (password) : 0;
788 size_t s3 = (s1 + s2) * 4 / 3 + 16;
789 if (s3 > len)
790 return 0;
791 xine_small_memcpy (dest + s3 - s2 - s1 - 1, user, s1);
792 dest[s3 - s2 - 1] = ':';
793 if (s2)
794 xine_small_memcpy (dest + s3 - s2, password, s2);
795 return xine_base64_encode ((uint8_t *)dest + s3 - s2 - s1 - 1, dest, s1 + s2 + 1);
796 }
797
http_plugin_read_metainf(http_input_plugin_t * this)798 static int http_plugin_read_metainf (http_input_plugin_t *this) {
799
800 char metadata_buf[256 * 16];
801 unsigned char len = 0;
802 char *title_end;
803 const char *songtitle;
804 const char *radio;
805 xine_event_t uevent;
806 xine_ui_data_t data;
807
808 /* get the length of the metadata */
809 if (sbuf_get_bytes (this, (uint8_t *)&len, 1) != 1)
810 return 0;
811
812 lprintf ("http_plugin_read_metainf: len=%d\n", len);
813
814 if (len > 0) {
815 if (sbuf_get_bytes (this, (uint8_t *)metadata_buf, len * 16) != (len * 16))
816 return 0;
817
818 metadata_buf[len * 16] = '\0';
819
820 lprintf ("http_plugin_read_metainf: %s\n", metadata_buf);
821
822 if (!this->stream)
823 return 1;
824
825 /* Extract the title of the current song */
826 if ((songtitle = strstr(metadata_buf, "StreamTitle="))) {
827 char terminator[] = { ';', 0, 0 };
828 songtitle += 12; /* skip "StreamTitle=" */
829 if (*songtitle == '\'' || *songtitle == '"') {
830 terminator[0] = *songtitle++;
831 terminator[1] = ';';
832 }
833 if ((title_end = strstr(songtitle, terminator))) {
834 *title_end = '\0';
835
836 if ((!this->shoutcast_songtitle ||
837 (strcmp(songtitle, this->shoutcast_songtitle))) &&
838 (strlen(songtitle) > 0)) {
839
840 lprintf ("http_plugin_read_metainf: songtitle: %s\n", songtitle);
841
842 free(this->shoutcast_songtitle);
843 this->shoutcast_songtitle = strdup(songtitle);
844
845 _x_meta_info_set(this->stream, XINE_META_INFO_TITLE, songtitle);
846
847 /* prepares the event */
848 radio = _x_meta_info_get(this->stream, XINE_META_INFO_ALBUM);
849 if (radio) {
850 snprintf (data.str, sizeof(data.str), "%s - %s", radio, songtitle);
851 } else {
852 strncpy(data.str, songtitle, sizeof(data.str)-1);
853 }
854 data.str[sizeof(data.str) - 1] = '\0';
855 data.str_len = strlen(data.str) + 1;
856
857 /* sends the event */
858 uevent.type = XINE_EVENT_UI_SET_TITLE;
859 uevent.stream = this->stream;
860 uevent.data = &data;
861 uevent.data_length = sizeof(data);
862 xine_event_send(this->stream, &uevent);
863 }
864 }
865 }
866 }
867 return 1;
868 }
869
870 /*
871 * Handle shoutcast packets
872 */
http_plugin_read_int(http_input_plugin_t * this,uint8_t * buf,size_t total)873 static ssize_t http_plugin_read_int (http_input_plugin_t *this, uint8_t *buf, size_t total) {
874 size_t read_bytes = 0;
875
876 lprintf("total=%"PRId64"\n", total);
877
878 if (this->mode & MODE_SHOUTCAST) {
879 /* shunt away info inserts */
880 while (total) {
881 ssize_t r;
882 if (total >= this->shoutcast_left) {
883 r = sbuf_get_bytes (this, buf + read_bytes, this->shoutcast_left);
884 if (r < 0)
885 goto error;
886 if (!http_plugin_read_metainf (this))
887 goto error;
888 this->shoutcast_left = this->shoutcast_interval;
889 } else {
890 r = sbuf_get_bytes (this, buf + read_bytes, total);
891 if (r < 0)
892 goto error;
893 this->shoutcast_left -= r;
894 /* end of file */
895 if (r == 0)
896 break;
897 }
898 read_bytes += r;
899 total -= r;
900 }
901 } else {
902 /* read as is */
903 ssize_t r = sbuf_get_bytes (this, buf, total);
904 if (r < 0)
905 goto error;
906 read_bytes += r;
907 }
908
909 if (this->mode & MODE_LASTFM) {
910 /* Identify SYNC string for last.fm, this is limited to last.fm
911 * streaming servers to avoid hitting on tracks metadata for other servers. */
912 if (read_bytes && memmem (buf, read_bytes, "SYNC", 4) && this->stream) {
913 /* Tell frontend to update the UI */
914 const xine_event_t event = {
915 .type = XINE_EVENT_UI_CHANNELS_CHANGED,
916 .stream = this->stream,
917 .data = NULL,
918 .data_length = 0
919 };
920 lprintf ("SYNC from last.fm server received\n");
921 xine_event_send (this->stream, &event);
922 }
923 }
924
925 return read_bytes;
926
927 error:
928 if (this->stream && !_x_action_pending(this->stream))
929 _x_message (this->stream, XINE_MSG_READ_ERROR, this->url.host, NULL);
930 xine_log (this->xine, XINE_LOG_MSG, _("input_http: read error %d\n"), errno);
931 return read_bytes;
932 }
933
http_plugin_read(input_plugin_t * this_gen,void * buf_gen,off_t nlen)934 static off_t http_plugin_read (input_plugin_t *this_gen, void *buf_gen, off_t nlen) {
935 http_input_plugin_t *this = (http_input_plugin_t *) this_gen;
936 char *buf = (char *)buf_gen;
937 size_t want, num_bytes;
938
939 if (nlen < 0)
940 return -1;
941
942 want = nlen;
943 if (want == 0)
944 return 0;
945
946 num_bytes = 0;
947 if (this->curpos < this->preview_size) {
948 uint32_t have = this->preview_size - this->curpos;
949 if (have > want)
950 have = want;
951 lprintf ("%u bytes from preview (which has %u bytes)\n", (unsigned int)have, (unsigned int)this->preview_size);
952 memcpy (buf, this->preview + this->curpos, have);
953 num_bytes += have;
954 want -= have;
955 this->curpos += have;
956 }
957
958 if (want > 0) {
959 ssize_t r = http_plugin_read_int (this, (uint8_t *)buf + num_bytes, want);
960 if (r > 0) {
961 num_bytes += r;
962 this->curpos += r;
963 }
964 }
965
966 return num_bytes;
967 }
968
http_plugin_get_length(input_plugin_t * this_gen)969 static off_t http_plugin_get_length (input_plugin_t *this_gen) {
970 http_input_plugin_t *this = (http_input_plugin_t *) this_gen;
971
972 return this->contentlength;
973 }
974
http_plugin_get_capabilities(input_plugin_t * this_gen)975 static uint32_t http_plugin_get_capabilities (input_plugin_t *this_gen) {
976 http_input_plugin_t *this = (http_input_plugin_t *) this_gen;
977 uint32_t caps = INPUT_CAP_PREVIEW | INPUT_CAP_SIZED_PREVIEW | INPUT_CAP_NEW_MRL;
978
979 /* Nullsoft asked to not allow saving streaming nsv files */
980 if (this->url.uri && strlen(this->url.uri) >= 4 &&
981 !strncmp(this->url.uri + strlen(this->url.uri) - 4, ".nsv", 4))
982 caps |= INPUT_CAP_RIP_FORBIDDEN;
983
984 if (this->mode & MODE_SEEKABLE) {
985 caps |= INPUT_CAP_SLOW_SEEKABLE;
986 }
987 return caps;
988 }
989
http_plugin_get_current_pos(input_plugin_t * this_gen)990 static off_t http_plugin_get_current_pos (input_plugin_t *this_gen){
991 http_input_plugin_t *this = (http_input_plugin_t *) this_gen;
992
993 return this->curpos;
994 }
995
http_close(http_input_plugin_t * this)996 static void http_close(http_input_plugin_t * this)
997 {
998 _x_tls_deinit (&this->tls);
999 if (this->fh >= 0) {
1000 _x_io_tcp_close (this->stream, this->fh);
1001 this->fh = -1;
1002 }
1003 _x_url_cleanup (&this->proxyurl);
1004 _x_url_cleanup (&this->url);
1005 }
1006
http_restart(http_input_plugin_t * this,off_t abs_offset)1007 static int http_restart(http_input_plugin_t * this, off_t abs_offset)
1008 {
1009 /* save old stream */
1010 xine_tls_t *old_tls = this->tls;
1011 off_t old_pos = this->curpos;
1012 int old_fh = this->fh;
1013
1014 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1015 LOG_MODULE ": seek to %" PRId64 ": reconnecting ...\n", (int64_t)abs_offset);
1016
1017 do {
1018 this->tls = NULL;
1019 this->fh = -1;
1020 _x_url_cleanup (&this->proxyurl);
1021 _x_url_cleanup (&this->url);
1022
1023 this->curpos = abs_offset;
1024 if (this->input_plugin.open (&this->input_plugin) != 1) {
1025 xprintf (this->xine, XINE_VERBOSITY_LOG,
1026 LOG_MODULE ": seek to %" PRId64 " failed (http request failed)\n", (int64_t)abs_offset);
1027 break;
1028 }
1029 if (this->curpos != abs_offset) {
1030 /* something went wrong ... */
1031 xprintf (this->xine, XINE_VERBOSITY_LOG,
1032 LOG_MODULE ": seek to %" PRId64 " failed (server returned invalid range)\n", (int64_t)abs_offset);
1033 break;
1034 }
1035
1036 /* close old connection */
1037 _x_tls_deinit (&old_tls);
1038 if (old_fh >= 0)
1039 _x_io_tcp_close (this->stream, old_fh);
1040 return 0;
1041 } while (0);
1042
1043 /* restore old stream */
1044 _x_tls_deinit (&this->tls);
1045 if (this->fh >= 0)
1046 _x_io_tcp_close (this->stream, this->fh);
1047 this->tls = old_tls;
1048 this->curpos = old_pos;
1049 this->fh = old_fh;
1050 return -1;
1051 }
1052
http_plugin_seek(input_plugin_t * this_gen,off_t offset,int origin)1053 static off_t http_plugin_seek(input_plugin_t *this_gen, off_t offset, int origin) {
1054 http_input_plugin_t *this = (http_input_plugin_t *) this_gen;
1055 off_t abs_offset;
1056
1057 abs_offset = _x_input_seek_preview(this_gen, offset, origin,
1058 &this->curpos, this->contentlength, this->preview_size);
1059
1060 if (abs_offset < 0 && (this->mode & MODE_SEEKABLE)) {
1061
1062 abs_offset = _x_input_translate_seek(offset, origin, this->curpos, this->contentlength);
1063 if (abs_offset < 0) {
1064 xprintf(this->xine, XINE_VERBOSITY_LOG,
1065 "input_http: invalid seek request (%d, %" PRId64 ")\n",
1066 origin, (int64_t)offset);
1067 return -1;
1068 }
1069
1070 if (http_restart(this, abs_offset) < 0)
1071 return -1;
1072 }
1073
1074 return abs_offset;
1075 }
1076
http_plugin_get_mrl(input_plugin_t * this_gen)1077 static const char* http_plugin_get_mrl (input_plugin_t *this_gen) {
1078 http_input_plugin_t *this = (http_input_plugin_t *) this_gen;
1079
1080 return this->mrl;
1081 }
1082
http_plugin_dispose(input_plugin_t * this_gen)1083 static void http_plugin_dispose (input_plugin_t *this_gen ) {
1084 http_input_plugin_t *this = (http_input_plugin_t *) this_gen;
1085
1086 http_close(this);
1087 sbuf_reset (this);
1088
1089 if (this->nbc) {
1090 nbc_close (this->nbc);
1091 this->nbc = NULL;
1092 }
1093
1094 if (this->head_dump_file) {
1095 fclose (this->head_dump_file);
1096 this->head_dump_file = NULL;
1097 }
1098
1099 free (this);
1100 }
1101
report_progress(xine_stream_t * stream,int p)1102 static void report_progress (xine_stream_t *stream, int p) {
1103
1104 xine_event_t event;
1105 xine_progress_data_t prg;
1106
1107 if (!stream)
1108 return;
1109
1110 prg.description = _("Connecting HTTP server...");
1111 prg.percent = p;
1112
1113 event.type = XINE_EVENT_PROGRESS;
1114 event.data = &prg;
1115 event.data_length = sizeof (xine_progress_data_t);
1116
1117 xine_event_send (stream, &event);
1118 }
1119
http_plugin_handshake(void * userdata,int fh)1120 static xio_handshake_status_t http_plugin_handshake (void *userdata, int fh) {
1121 static const uint8_t tab_tolower[256] = {
1122 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
1123 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
1124 ' ','!','"','#','$','%','&','\'','(',')','*','+',',','-','.','/',
1125 '0','1','2','3','4','5','6','7','8','9',':',';','<','=','>','?',
1126 '@','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
1127 'p','q','r','s','t','u','v','w','x','y','z','[','\\',']','^','_',
1128 '`','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',
1129 'p','q','r','s','t','u','v','w','x','y','z','{','|','}','~',127,
1130 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
1131 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
1132 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
1133 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
1134 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
1135 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
1136 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
1137 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
1138 };
1139
1140 http_input_plugin_t *this = (http_input_plugin_t *)userdata;
1141 http_input_class_t *this_class = (http_input_class_t *) this->input_plugin.input_class;
1142 int res;
1143 int mpegurl_redirect = 0;
1144 char mime_type[128];
1145
1146 {
1147 char httpstatus[128];
1148 httpstatus[0] = 0;
1149 mime_type[0] = 0;
1150 sbuf_reset (this);
1151
1152 {
1153 uint32_t timeout, progress;
1154 timeout = _x_query_network_timeout (this->xine) * 1000;
1155 if (timeout == 0)
1156 timeout = 30000;
1157 progress = 0;
1158 do {
1159 if (this->num_msgs) {
1160 if (this->num_msgs > 0)
1161 this->num_msgs--;
1162 report_progress (this->stream, progress);
1163 }
1164 res = _x_io_select (this->stream, fh, XIO_WRITE_READY, 500);
1165 progress += (500 * 100000) / timeout;
1166 } while ((res == XIO_TIMEOUT) && (progress <= 100000) && !_x_action_pending (this->stream));
1167 if (res != XIO_READY) {
1168 _x_message (this->stream, XINE_MSG_NETWORK_UNREACHABLE, this->mrl, NULL);
1169 this->ret = -3;
1170 return XIO_HANDSHAKE_TRY_NEXT;
1171 }
1172 }
1173
1174 /* TLS */
1175 _x_assert (this->tls == NULL);
1176 this->tls = _x_tls_init (this->xine, this->stream, fh);
1177 if (!this->tls) {
1178 this->ret = -2;
1179 return XIO_HANDSHAKE_INTR;
1180 }
1181 if (this->use_tls) {
1182 int r = _x_tls_handshake (this->tls, this->url.host, -1);
1183 if (r < 0) {
1184 _x_message (this->stream, XINE_MSG_CONNECTION_REFUSED, "TLS handshake failed", NULL);
1185 xprintf (this->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": TLS handshake failed\n");
1186 this->ret = -4;
1187 _x_tls_deinit (&this->tls);
1188 return XIO_HANDSHAKE_TRY_NEXT;
1189 }
1190 xprintf (this->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": TLS handshake succeed, connection is encrypted\n");
1191 }
1192
1193 /* Request */
1194 {
1195 /* total size of string literals: tfi input_http.c -x 0 "ADDLIT%q(%22%r%22)" "%r" -k -L (or just count yourself ;-) */
1196 #define SIZEOF_LITERALS 205
1197 /* max size needed for numbers */
1198 #define SIZEOF_NUMS (1 * 24)
1199 #define ADDLIT(s) { static const char ls[] = s; memcpy (q, s, sizeof (ls)); q += sizeof (ls) - 1; }
1200 #define ADDSTR(s) q += strlcpy (q, s, e - q); if (q > e) q = e
1201 char *q = (char *)this->sbuf, *e = q + sizeof (this->sbuf) - SIZEOF_LITERALS - SIZEOF_NUMS - 1;
1202 char strport[16];
1203 int vers = this_class->prot_version;
1204
1205 if (this->url.port != DEFAULT_HTTP_PORT) {
1206 char *t = strport;
1207 *t++ = ':';
1208 uint32_2str (&t, this->url.port);
1209 } else {
1210 strport[0] = 0;
1211 }
1212
1213 ADDLIT ("GET ");
1214 if (this->use_proxy) {
1215 ADDSTR (this->url.proto);
1216 ADDLIT ("://");
1217 ADDSTR (this->url.host);
1218 ADDSTR (strport);
1219 }
1220 ADDSTR (this->url.uri);
1221 if (vers == 1) {
1222 ADDLIT (" HTTP/1.1\r\nHost: ");
1223 } else {
1224 ADDLIT (" HTTP/1.0\r\nHost: ");
1225 }
1226 ADDSTR (this->url.host);
1227 ADDSTR (strport);
1228 if (this->curpos > 0) {
1229 /* restart from offset */
1230 ADDLIT ("\r\nRange: bytes=");
1231 uint64_2str (&q, this->curpos);
1232 ADDLIT ("-");
1233 /* uint64_2str (&q, this->contentlength - 1); */
1234 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1235 "input_http: requesting restart from offset %" PRId64 "\n", (int64_t)this->curpos);
1236 } else if (vers == 1) {
1237 ADDLIT ("\r\nAccept-Encoding: gzip,deflate");
1238 }
1239 if (this->use_proxy && this_class->proxyuser && this_class->proxyuser[0]) {
1240 ADDLIT ("\r\nProxy-Authorization: Basic ");
1241 q += http_plugin_basicauth (this_class->proxyuser, this_class->proxypassword, q, e - q);
1242 }
1243 if (this->url.user && this->url.user[0]) {
1244 ADDLIT ("\r\nAuthorization: Basic ");
1245 q += http_plugin_basicauth (this->url.user, this->url.password, q, e - q);
1246 }
1247 ADDLIT ("\r\nUser-Agent: ");
1248 if (this->user_agent) {
1249 ADDSTR (this->user_agent);
1250 ADDLIT (" ");
1251 }
1252 ADDLIT ("xine/" VERSION "\r\nAccept: */*\r\nIcy-MetaData: 1\r\n\r\n");
1253 if (this->head_dump_file)
1254 fwrite (this->sbuf, 1, (uint8_t *)q - this->sbuf, this->head_dump_file);
1255 if (_x_tls_write (this->tls, this->sbuf, (uint8_t *)q - this->sbuf) != (uint8_t *)q - this->sbuf) {
1256 _x_message (this->stream, XINE_MSG_CONNECTION_REFUSED, "couldn't send request", NULL);
1257 xprintf (this->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": couldn't send request\n");
1258 this->ret = -4;
1259 _x_tls_deinit (&this->tls);
1260 return XIO_HANDSHAKE_TRY_NEXT;
1261 }
1262 lprintf ("request sent: >%s<\n", this->sbuf);
1263 #undef ADDSTR
1264 #undef ADDLIT
1265 #undef SIZEOF_LITERALS
1266 #undef SIZEOF_NUMS
1267 }
1268
1269 this->mode &= ~(MODE_DONE | MODE_NSV | MODE_LASTFM | MODE_SHOUTCAST | MODE_AGAIN);
1270
1271 /* Response */
1272 do {
1273 this->mode &= ~(MODE_HAS_TYPE | MODE_HAS_LENGTH | MODE_DEFLATED | MODE_CHUNKED | MODE_HAVE_CHUNK | MODE_HAVE_SBUF);
1274 this->range_start = 0;
1275 this->range_end = 0;
1276 this->range_total = 0;
1277 /* get status */
1278 {
1279 uint8_t *line, *p1, *p2;
1280 int32_t ok = 0, i = sbuf_get_string (this, &line);
1281 if ((i < 0) && (errno == EINTR)) {
1282 this->ret = -1;
1283 _x_tls_deinit (&this->tls);
1284 return XIO_HANDSHAKE_INTR;
1285 }
1286 do {
1287 if (i < 4)
1288 break;
1289 if ((memcmp (line, "HTTP", 4) && memcmp (line, "ICY ", 4)))
1290 break;
1291 p1 = line + i;
1292 *p1 = ' ';
1293 for (p2 = line + 4; *p2 != ' '; p2++) ;
1294 *p1 = 0;
1295 while (*p2 == ' ') p2++;
1296 this->status = str2uint32 (&p2);
1297 if (this->status == 0)
1298 break;
1299 while (*p2 == ' ') p2++;
1300 strlcpy (httpstatus, (char *)p2, sizeof (httpstatus));
1301 ok = 1;
1302 } while (0);
1303 if (!ok) {
1304 _x_message (this->stream, XINE_MSG_CONNECTION_REFUSED, "invalid http answer", NULL);
1305 xine_log (this->xine, XINE_LOG_MSG, _("input_http: invalid http answer\n"));
1306 this->ret = -6;
1307 _x_tls_deinit (&this->tls);
1308 return XIO_HANDSHAKE_TRY_NEXT;
1309 }
1310 }
1311 /* get props */
1312 while (1) {
1313 uint32_t key;
1314 uint8_t *line, *p1, *p2;
1315 {
1316 int32_t i = sbuf_get_string (this, &line);
1317 if (i < 0) {
1318 /* "httpget.http: invalid HTTP response\n" */
1319 this->ret = -1;
1320 _x_tls_deinit (&this->tls);
1321 return errno == EINTR ? XIO_HANDSHAKE_INTR : XIO_HANDSHAKE_TRY_NEXT;
1322 }
1323 /* an empty line marks the end of response head */
1324 if (!i) break;
1325 p1 = line + i;
1326 }
1327 /* find value string */
1328 *p1 = ':';
1329 for (p2 = line; *p2 != ':'; p2++) *p2 = tab_tolower[*p2];
1330 *p1 = 0;
1331 if (p2 != p1) {
1332 *p2++ = 0;
1333 while (*p2 == ' ') p2++;
1334 }
1335 /* find key */
1336 {
1337 static const char * const keys[] = {
1338 "\x06""accept-ranges",
1339 "\x03""content-encoding",
1340 "\x01""content-length",
1341 "\x04""content-range",
1342 "\x02""content-type",
1343 "\x0b""icy-genre",
1344 "\x0d""icy-metaint",
1345 "\x0a""icy-name",
1346 "\x0c""icy-notice2",
1347 "\x07""location",
1348 "\x08""server",
1349 "\x05""transfer-encoding",
1350 "\x09""www-authenticate"
1351 };
1352 uint32_t b = 0, e = sizeof (keys) / sizeof (keys[0]), m = e >> 1;
1353 key = 0;
1354 do {
1355 int d = strcmp ((char *)line, keys[m] + 1);
1356 if (d == 0) {
1357 key = keys[m][0];
1358 break;
1359 }
1360 if (d < 0)
1361 e = m;
1362 else
1363 b = m + 1;
1364 m = (b + e) >> 1;
1365 } while (b != e);
1366 }
1367 switch (key) {
1368 case 0x1: /* content-length */
1369 if (!this->contentlength)
1370 this->contentlength = str2uint64 (&p2);
1371 this->mode |= MODE_HAS_LENGTH;
1372 break;
1373 case 0x2: /* content type */
1374 strlcpy (mime_type, (char *)p2, sizeof (mime_type));
1375 this->mode |= MODE_HAS_TYPE;
1376 if (!strncasecmp ((char *)p2, "audio/x-mpegurl", 15)) {
1377 lprintf ("Opening an audio/x-mpegurl file, late redirect.");
1378 mpegurl_redirect = 1;
1379 }
1380 else if (!strncasecmp ((char *)p2, "video/nsv", 9)) {
1381 lprintf ("shoutcast nsv detected\n");
1382 this->mode |= MODE_NSV;
1383 }
1384 break;
1385 case 0x3: /* content-encoding */
1386 if ((!memcmp (p2, "gzip", 4)) || (!memcmp (p2, "deflate", 7)))
1387 this->mode |= MODE_DEFLATED;
1388 break;
1389 case 0x4: /* content-range */
1390 if (!memcmp (p2, "bytes", 5))
1391 p2 += 5;
1392 while (*p2 == ' ')
1393 p2++;
1394 this->range_start = str2uint64 (&p2);
1395 if (*p2 == '-') {
1396 p2++;
1397 this->range_end = str2uint64 (&p2);
1398 }
1399 if (*p2 == '/') {
1400 p2++;
1401 this->range_total = str2uint64 (&p2);
1402 }
1403 break;
1404 case 0x5: /* transfer-encoding */
1405 if ((!memcmp (p2, "chunked", 7)))
1406 this->mode |= MODE_CHUNKED | MODE_HAVE_CHUNK;
1407 break;
1408 case 0x6: /* accept-ranges */
1409 if (strstr ((char *)line + 14, "bytes")) {
1410 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1411 "input_http: Server supports request ranges. Enabling seeking support.\n");
1412 this->mode |= MODE_SEEKABLE;
1413 } else {
1414 xprintf (this->xine, XINE_VERBOSITY_LOG,
1415 "input_http: Unknown value in header \'%s\'\n", line + 14);
1416 this->mode &= ~MODE_SEEKABLE;
1417 }
1418 break;
1419 case 0x7: /* location */
1420 /* check redirection */
1421 if ((this->status == 302) /* found */
1422 || (this->status == 301) /* moved permanently */
1423 || (this->status == 303) /* see other */
1424 || (this->status == 307)) { /* temporary redirect */
1425 lprintf ("trying to open target of redirection: >%s<\n", (char *)p2);
1426 _x_merge_mrl (this->mrl, sizeof (this->mrl), this->mrl, (char *)p2);
1427 }
1428 break;
1429 case 0x8: /* server */
1430 if (!strncasecmp ((char *)p2, "last.fm", 7)) {
1431 lprintf ("last.fm streaming server detected\n");
1432 this->mode |= MODE_LASTFM;
1433 }
1434 break;
1435 case 0x9: /* www-authenticate */
1436 if (this->status == 401)
1437 _x_message (this->stream, XINE_MSG_AUTHENTICATION_NEEDED, this->mrl, (char *)p2, NULL);
1438 break;
1439 /* Icecast / ShoutCast Stuff */
1440 case 0xa: /* icy-name */
1441 if (this->stream) {
1442 _x_meta_info_set (this->stream, XINE_META_INFO_ALBUM, (char *)p2);
1443 _x_meta_info_set (this->stream, XINE_META_INFO_TITLE, (char *)p2);
1444 }
1445 break;
1446 case 0xb: /* icy-genre */
1447 if (this->stream)
1448 _x_meta_info_set (this->stream, XINE_META_INFO_GENRE, (char *)p2);
1449 break;
1450 /* icy-notice1 is always the same */
1451 case 0xc: /* icy-notice2 */
1452 {
1453 char *end = strstr ((char *)p2, "<BR>");
1454 if (end)
1455 *end = '\0';
1456 if (this->stream)
1457 _x_meta_info_set (this->stream, XINE_META_INFO_COMMENT, (char *)p2);
1458 }
1459 break;
1460 case 0xd: /* icy-metaint */
1461 /* metadata interval (in byte) */
1462 this->shoutcast_interval = this->shoutcast_left = str2uint32 (&p2);
1463 lprintf ("shoutcast_interval: %d\n", this->shoutcast_interval);
1464 if (this->shoutcast_interval)
1465 this->mode |= MODE_SHOUTCAST;
1466 break;
1467 }
1468 }
1469 if (this->sgot > this->sdelivered)
1470 this->mode |= MODE_HAVE_SBUF;
1471 /* skip non-document content */
1472 if ((this->status != 200) && (this->status != 206)) while (this->contentlength > 0) {
1473 ssize_t s = sizeof (this->preview);
1474 if ((uint64_t)s > this->contentlength) s = this->contentlength;
1475 s = sbuf_get_bytes (this, this->preview, s);
1476 if (s <= 0) break;
1477 this->contentlength -= s;
1478 }
1479 /* indicate free content length */
1480 if ((this->mode & (MODE_HAS_TYPE | MODE_HAS_LENGTH)) == MODE_HAS_TYPE) this->contentlength = ~(uint64_t)0;
1481 if (this->mode & MODE_CHUNKED) this->contentlength = ~(uint64_t)0;
1482 } while (this->status == 102); /* processing */
1483
1484 this->curpos = 0;
1485
1486 switch (this->status / 100) {
1487 case 2:
1488 break;
1489 case 3:
1490 xine_log (this->xine, XINE_LOG_MSG,
1491 _("input_http: 3xx redirection: >%d %s<\n"), this->status, httpstatus);
1492 this->mode |= MODE_AGAIN;
1493 break;
1494 case 4:
1495 if (this->status == 404) { /* not found */
1496 _x_message (this->stream, XINE_MSG_FILE_NOT_FOUND, this->mrl, NULL);
1497 xine_log (this->xine, XINE_LOG_MSG,
1498 _("input_http: http status not 2xx: >%d %s<\n"), this->status, httpstatus);
1499 this->ret = -7;
1500 _x_tls_deinit (&this->tls);
1501 return XIO_HANDSHAKE_INTR;
1502 }
1503 if (this->status == 401) {
1504 xine_log (this->xine, XINE_LOG_MSG,
1505 _("input_http: http status not 2xx: >%d %s<\n"), this->status, httpstatus);
1506 /* don't return - there may be a WWW-Authenticate header... */
1507 break;
1508 }
1509 if (this->status == 403) {
1510 _x_message (this->stream, XINE_MSG_PERMISSION_ERROR, this->mrl, NULL);
1511 xine_log (this->xine, XINE_LOG_MSG,
1512 _("input_http: http status not 2xx: >%d %s<\n"), this->status, httpstatus);
1513 this->ret = -8;
1514 _x_tls_deinit (&this->tls);
1515 return XIO_HANDSHAKE_INTR;
1516 }
1517 /* fall through */
1518 default:
1519 _x_message (this->stream, XINE_MSG_CONNECTION_REFUSED, "http status not 2xx: ", httpstatus, NULL);
1520 xine_log (this->xine, XINE_LOG_MSG,
1521 _("input_http: http status not 2xx: >%d %s<\n"), this->status, httpstatus);
1522 this->ret = -9;
1523 _x_tls_deinit (&this->tls);
1524 return XIO_HANDSHAKE_TRY_NEXT;
1525 }
1526
1527 if (this->mode & MODE_HAS_LENGTH)
1528 xine_log (this->xine, XINE_LOG_MSG,
1529 _("input_http: content length = %" PRId64 " bytes\n"), (int64_t)this->contentlength);
1530 if (this->range_start) {
1531 this->curpos = this->range_start;
1532 if (this->contentlength != this->range_end + 1) {
1533 xprintf (this->xine, XINE_VERBOSITY_LOG,
1534 "input_http: Reveived invalid content range");
1535 /* truncate - there won't be more data anyway */
1536 this->contentlength = this->range_end + 1;
1537 } else {
1538 xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1539 "input_http: Stream starting at offset %" PRIu64 "\n.", this->range_start);
1540 }
1541 }
1542 #if 0
1543 if ( len >= (int)sizeof(buf) ) {
1544 _x_message(this->stream, XINE_MSG_PERMISSION_ERROR, this->mrl, NULL);
1545 xine_log (this->xine, XINE_LOG_MSG,
1546 _("input_http: buffer exhausted after %zu bytes."), sizeof(buf));
1547 this->ret = -10;
1548 _x_tls_deinit (&this->tls);
1549 return XIO_HANDSHAKE_TRY_NEXT;
1550 }
1551 #endif
1552 lprintf ("end of headers\n");
1553
1554 if (mpegurl_redirect) {
1555 ssize_t l = sbuf_get_bytes (this, this->preview, sizeof (this->preview) - 1);
1556 if (l > 0) {
1557 uint8_t *p = this->preview;
1558 p[l] = 0;
1559 while (p[0] & 0xe0)
1560 p++;
1561 /* If the newline can't be found, either the 4K buffer is too small, or
1562 * more likely something is fuzzy. */
1563 if (p < this->preview + l) {
1564 *p = 0;
1565 lprintf ("mpegurl pointing to %s\n", (char *)this->preview);
1566 _x_merge_mrl (this->mrl, sizeof (this->mrl), this->mrl, (char *)this->preview);
1567 this->mode |= MODE_AGAIN;
1568 }
1569 }
1570 }
1571 }
1572
1573 strcpy (this->mime_type, mime_type);
1574
1575 return XIO_HANDSHAKE_OK;
1576 }
1577
http_plugin_open(input_plugin_t * this_gen)1578 static int http_plugin_open (input_plugin_t *this_gen) {
1579 http_input_plugin_t *this = (http_input_plugin_t *)this_gen;
1580 http_input_class_t *this_class = (http_input_class_t *) this->input_plugin.input_class;
1581 int redirections = 20;
1582
1583 do {
1584 int proxyport, mrl_tls, proxy_tls;
1585
1586 http_close (this);
1587
1588 if (--redirections <= 0) {
1589 xprintf (this->xine, XINE_VERBOSITY_LOG,
1590 "input_http: too many redirections, giving up.\n");
1591 return -1;
1592 }
1593
1594 this->user_agent = _x_url_user_agent (this->mrl);
1595 if (!_x_url_parse2 (this->mrl, &this->url)) {
1596 _x_message (this->stream, XINE_MSG_GENERAL_WARNING, "malformed url", NULL);
1597 return 0;
1598 }
1599 /* NOTE: mrl = http | https
1600 * proxy = http | https
1601 * all 4 combinations are valid! */
1602 this->use_tls = mrl_tls = !strcasecmp (this->url.proto, "https");
1603 if (this->url.port == 0)
1604 this->url.port = mrl_tls ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT;
1605
1606 this->use_proxy = (this_class->proxyhost && this_class->proxyhost[0]);
1607 if (this->use_proxy && !_x_use_proxy (this->xine, this_class, this->url.host))
1608 this->use_proxy = 0;
1609 if (this->use_proxy && !_x_url_parse2 (this_class->proxyhost, &this->proxyurl)) {
1610 _x_message (this->stream, XINE_MSG_GENERAL_WARNING, "malformed proxy url", NULL);
1611 this->use_proxy = 0;
1612 }
1613 proxyport = this_class->proxyport;
1614 if (this->use_proxy) {
1615 this->use_tls = proxy_tls = !strcasecmp (this->proxyurl.proto, "https");
1616 if (proxyport == 0)
1617 proxyport = proxy_tls ? DEFAULT_HTTPS_PORT : DEFAULT_HTTP_PORT;
1618 }
1619
1620 #ifdef LOG
1621 printf ("input_http: host : >%s<\n", this->url.host);
1622 printf ("input_http: port : >%d<\n", this->url.port);
1623 printf ("input_http: user : >%s<\n", this->url.user);
1624 printf ("input_http: password : >%s<\n", this->url.password);
1625 printf ("input_http: path : >%s<\n", this->url.uri);
1626 if (this->use_proxy)
1627 printf (" via proxy >%s:%d<", this_class->proxyhost, proxyport);
1628 printf ("\n");
1629 #endif
1630
1631 this->bytes_left = ~(uint64_t)0;
1632 this->ret = -2;
1633 if (this->use_proxy)
1634 this->fh = _x_io_tcp_handshake_connect (this->stream, this_class->proxyhost, proxyport, http_plugin_handshake, this);
1635 else
1636 this->fh = _x_io_tcp_handshake_connect (this->stream, this->url.host, this->url.port, http_plugin_handshake, this);
1637 if (this->fh < 0)
1638 return this->ret;
1639 } while (this->mode & MODE_AGAIN);
1640
1641 if (this->contentlength != ~(uint64_t)0)
1642 this->bytes_left = this->contentlength - this->curpos - this->sgot + this->sdelivered;
1643 else
1644 this->contentlength = 0;
1645 if (this->mode & MODE_DEFLATED)
1646 this->contentlength = 0;
1647
1648 if (this->head_dump_file)
1649 fflush (this->head_dump_file);
1650
1651 if (this->curpos > 0) {
1652 /* restarting after seek */
1653 this->preview_size = 0;
1654 return 1;
1655 }
1656
1657 /* fill preview buffer */
1658 this->preview_size = http_plugin_read_int (this, this->preview, MAX_PREVIEW_SIZE);
1659 if (this->mode & MODE_NSV) {
1660 #define V_NSV (('N' << 24) | ('S' << 16) | ('V' << 8))
1661 int32_t max_bytes = 1 << 20;
1662 uint32_t v = 0;
1663 uint8_t *p = this->preview, *e = p + this->preview_size;
1664 lprintf ("resyncing NSV stream\n");
1665 while (this->preview_size > 2) {
1666 if ((max_bytes -= this->preview_size) <= 0)
1667 break;
1668 while (p < e) {
1669 v = (v | *p++) << 8;
1670 if (v == V_NSV)
1671 break;
1672 }
1673 if (v == V_NSV)
1674 break;
1675 this->preview[0] = e[-2];
1676 this->preview[1] = e[-1];
1677 this->preview_size = http_plugin_read_int (this, this->preview + 2, MAX_PREVIEW_SIZE - 2);
1678 p = this->preview + 2;
1679 e = p + this->preview_size;
1680 }
1681 if (v != V_NSV) {
1682 xprintf (this->xine, XINE_VERBOSITY_DEBUG, "http: cannot resync NSV stream!\n");
1683 _x_tls_deinit (&this->tls);
1684 _x_io_tcp_close (this->stream, this->fh);
1685 this->fh = -1;
1686 return -11;
1687 }
1688 this->preview_size = e - p + 3;
1689 if (p - 3 > this->preview)
1690 memmove (this->preview, p - 3, this->preview_size);
1691 if (this->preview_size < MAX_PREVIEW_SIZE) {
1692 int32_t r = http_plugin_read_int (this, this->preview + this->preview_size, MAX_PREVIEW_SIZE - this->preview_size);
1693 if (r > 0)
1694 this->preview_size += r;
1695 }
1696 lprintf ("NSV stream resynced\n");
1697 }
1698 if (this->preview_size < 0) {
1699 this->preview_size = 0;
1700 xine_log (this->xine, XINE_LOG_MSG, _("input_http: read error %d\n"), errno);
1701 _x_tls_deinit (&this->tls);
1702 _x_io_tcp_close (this->stream, this->fh);
1703 this->fh = -1;
1704 return -12;
1705 }
1706 lprintf ("preview_size=%d\n", this->preview_size);
1707 this->curpos = 0;
1708
1709 this->ret = 1;
1710 return 1;
1711 }
1712
http_can_handle(xine_stream_t * stream,const char * mrl)1713 static int http_can_handle (xine_stream_t *stream, const char *mrl) {
1714 if (!strncasecmp (mrl, "https://", 8)) {
1715 /* check for tls plugin here to allow trying another https plugin (avio) */
1716 if (!_x_tls_available(stream->xine)) {
1717 xine_log (stream->xine, XINE_LOG_MSG, "input_http: TLS plugin not found\n");
1718 return 0;
1719 }
1720 } else
1721 if (strncasecmp (mrl, "http://", 7) &&
1722 strncasecmp (mrl, "unsv://", 7) &&
1723 strncasecmp (mrl, "peercast://pls/", 15) &&
1724 !_x_url_user_agent (mrl) /* user agent hacks */) {
1725 return 0;
1726 }
1727 return 1;
1728 }
1729
http_plugin_get_optional_data(input_plugin_t * this_gen,void * const data,int data_type)1730 static int http_plugin_get_optional_data (input_plugin_t *this_gen,
1731 void *const data, int data_type) {
1732
1733 void **const ptr = (void **const) data;
1734 http_input_plugin_t *this = (http_input_plugin_t *) this_gen;
1735
1736 switch (data_type) {
1737 case INPUT_OPTIONAL_DATA_PREVIEW:
1738 if (!data || (this->preview_size <= 0))
1739 break;
1740 memcpy (data, this->preview, this->preview_size);
1741 return this->preview_size;
1742
1743 case INPUT_OPTIONAL_DATA_SIZED_PREVIEW:
1744 if (!data || (this->preview_size <= 0))
1745 break;
1746 {
1747 int want;
1748 memcpy (&want, data, sizeof (want));
1749 want = want < 0 ? 0
1750 : want > this->preview_size ? this->preview_size
1751 : want;
1752 memcpy (data, this->preview, want);
1753 return want;
1754 }
1755
1756 case INPUT_OPTIONAL_DATA_MIME_TYPE:
1757 *ptr = this->mime_type;
1758 /* fall through */
1759 case INPUT_OPTIONAL_DATA_DEMUX_MIME_TYPE:
1760 return *this->mime_type ? INPUT_OPTIONAL_SUCCESS : INPUT_OPTIONAL_UNSUPPORTED;
1761
1762 case INPUT_OPTIONAL_DATA_NEW_MRL:
1763 if (!data)
1764 break;
1765 if (!http_can_handle (this->stream, data))
1766 break;
1767 http_close (this);
1768 sbuf_reset (this);
1769 this->mrl[0] = 0;
1770 this->mime_type[0] = 0;
1771 _x_freep (&this->user_agent);
1772 _x_freep (&this->shoutcast_songtitle);
1773 this->curpos = 0;
1774 this->contentlength = 0;
1775 this->mode &= ~(MODE_DONE | MODE_SEEKABLE | MODE_NSV | MODE_LASTFM | MODE_SHOUTCAST);
1776 this->shoutcast_interval = 0;
1777 this->shoutcast_left = 0;
1778 this->preview_size = 0;
1779 if ((this->num_msgs < 0) || (this->num_msgs > 8))
1780 this->num_msgs = 8;
1781 if (!strncasecmp ((const char *)data, "peercast://pls/", 15)) {
1782 char *w = this->mrl, *e = w + sizeof (this->mrl);
1783 w += strlcpy (w, "http://127.0.0.1:7144/stream/", e - w);
1784 strlcpy (w, (const char *)data + 15, e - w);
1785 } else
1786 strlcpy (this->mrl, (const char *)data, sizeof (this->mrl));
1787 return INPUT_OPTIONAL_SUCCESS;
1788 }
1789
1790 return INPUT_OPTIONAL_UNSUPPORTED;
1791 }
1792
1793 /*
1794 * http input plugin class
1795 */
http_class_get_instance(input_class_t * cls_gen,xine_stream_t * stream,const char * mrl)1796 static input_plugin_t *http_class_get_instance (input_class_t *cls_gen, xine_stream_t *stream,
1797 const char *mrl) {
1798 http_input_class_t *cls = (http_input_class_t *)cls_gen;
1799 http_input_plugin_t *this;
1800
1801 if (!http_can_handle (stream, mrl))
1802 return NULL;
1803
1804 this = calloc(1, sizeof(http_input_plugin_t));
1805 if (!this)
1806 return NULL;
1807
1808 #ifndef HAVE_ZERO_SAFE_MEM
1809 this->curpos = 0;
1810 this->contentlength = 0;
1811 this->mime_type[0] = 0;
1812 this->user_agent = NULL;
1813 this->tls = NULL;
1814 this->mode = 0;
1815 this->shoutcast_interval = 0;
1816 this->shoutcast_left = 0;
1817 this->shoutcast_songtitle = NULL;
1818 this->preview_size = 0;
1819 this->url.proto = NULL;
1820 this->url.host = NULL;
1821 this->url.user = NULL;
1822 this->url.password = NULL;
1823 this->url.uri = NULL;
1824 this->proxyurl.proto = NULL;
1825 this->proxyurl.host = NULL;
1826 this->proxyurl.user = NULL;
1827 this->proxyurl.password = NULL;
1828 this->proxyurl.uri = NULL;
1829 this->head_dump_file = NULL;
1830 #endif
1831
1832 if (!strncasecmp (mrl, "peercast://pls/", 15)) {
1833 char *w = this->mrl, *e = w + sizeof (this->mrl);
1834 w += strlcpy (w, "http://127.0.0.1:7144/stream/", e - w);
1835 strlcpy (w, mrl + 15, e - w);
1836 } else {
1837 strlcpy (this->mrl, mrl, sizeof (this->mrl));
1838 }
1839
1840 this->fh = -1;
1841
1842 this->num_msgs = -1;
1843 this->stream = stream;
1844 this->xine = cls->xine;
1845 this->nbc = stream ? nbc_init (stream) : NULL;
1846 sbuf_init (this);
1847
1848 if (cls->head_dump_name && cls->head_dump_name[0]) {
1849 this->head_dump_file = fopen (cls->head_dump_name, "ab");
1850 if (this->head_dump_file)
1851 fseek (this->head_dump_file, 0, SEEK_END);
1852 }
1853
1854 this->input_plugin.open = http_plugin_open;
1855 this->input_plugin.get_capabilities = http_plugin_get_capabilities;
1856 this->input_plugin.read = http_plugin_read;
1857 this->input_plugin.read_block = _x_input_default_read_block;
1858 this->input_plugin.seek = http_plugin_seek;
1859 this->input_plugin.get_current_pos = http_plugin_get_current_pos;
1860 this->input_plugin.get_length = http_plugin_get_length;
1861 this->input_plugin.get_blocksize = _x_input_default_get_blocksize;
1862 this->input_plugin.get_mrl = http_plugin_get_mrl;
1863 this->input_plugin.get_optional_data = http_plugin_get_optional_data;
1864 this->input_plugin.dispose = http_plugin_dispose;
1865 this->input_plugin.input_class = cls_gen;
1866
1867 return &this->input_plugin;
1868 }
1869
http_class_dispose(input_class_t * this_gen)1870 static void http_class_dispose (input_class_t *this_gen) {
1871 http_input_class_t *this = (http_input_class_t *) this_gen;
1872 config_values_t *config = this->xine->config;
1873
1874 config->unregister_callbacks (config, NULL, NULL, this, sizeof (*this));
1875
1876 free (this);
1877 }
1878
input_http_init_class(xine_t * xine,const void * data)1879 void *input_http_init_class (xine_t *xine, const void *data) {
1880 http_input_class_t *this;
1881 config_values_t *config;
1882 char *proxy_env;
1883 char *proxyhost_env = NULL;
1884 int proxyport_env = DEFAULT_HTTP_PORT;
1885
1886 (void)data;
1887 this = calloc(1, sizeof (http_input_class_t));
1888 if (!this)
1889 return NULL;
1890
1891 this->xine = xine;
1892 config = xine->config;
1893
1894 this->input_class.get_instance = http_class_get_instance;
1895 this->input_class.identifier = "http";
1896 this->input_class.description = N_("http/https input plugin");
1897 this->input_class.get_dir = NULL;
1898 this->input_class.get_autoplay_list = NULL;
1899 this->input_class.dispose = http_class_dispose;
1900 this->input_class.eject_media = NULL;
1901
1902 /*
1903 * honour http_proxy envvar
1904 */
1905 if((proxy_env = getenv("http_proxy")) && *proxy_env) {
1906 char *p;
1907
1908 if(!strncmp(proxy_env, "http://", 7))
1909 proxy_env += 7;
1910
1911 proxyhost_env = strdup(proxy_env);
1912
1913 if((p = strrchr(proxyhost_env, ':')) && (strlen(p) > 1)) {
1914 *p++ = '\0';
1915 proxyport_env = (int) strtol(p, &p, 10);
1916 }
1917 }
1918
1919 /*
1920 * proxy settings
1921 */
1922 this->proxyhost = config->register_string (config, "media.network.http_proxy_host",
1923 proxyhost_env ? proxyhost_env : "",
1924 _("HTTP proxy host"),
1925 _("The hostname of the HTTP proxy."),
1926 10, proxy_host_change_cb, (void *) this);
1927
1928 this->proxyport = config->register_num (config, "media.network.http_proxy_port",
1929 proxyport_env,
1930 _("HTTP proxy port"),
1931 _("The port number of the HTTP proxy."),
1932 10, proxy_port_change_cb, (void *) this);
1933
1934 /* registered entries could be empty. Don't ignore envvar */
1935 if(!strlen(this->proxyhost) && (proxyhost_env && strlen(proxyhost_env))) {
1936 config->update_string(config, "media.network.http_proxy_host", proxyhost_env);
1937 config->update_num(config, "media.network.http_proxy_port", proxyport_env);
1938 }
1939 _x_freep(&proxyhost_env);
1940
1941 this->proxyuser = config->register_string (config, "media.network.http_proxy_user",
1942 "",
1943 _("HTTP proxy username"),
1944 _("The user name for the HTTP proxy."),
1945 10, proxy_user_change_cb, (void *) this);
1946
1947 this->proxypassword = config->register_string (config, "media.network.http_proxy_password",
1948 "",
1949 _("HTTP proxy password"),
1950 _("The password for the HTTP proxy."),
1951 10, proxy_password_change_cb, (void *) this);
1952
1953 this->noproxylist = config->register_string (config, "media.network.http_no_proxy",
1954 "",
1955 _("Domains for which to ignore the HTTP proxy"),
1956 _("A comma-separated list of domain names for which the proxy is to be ignored.\n"
1957 "If a domain name is prefixed with '=' then it is treated as a host name only "
1958 "(full match required)."),
1959 10, no_proxy_list_change_cb, (void *) this);
1960
1961 /* protocol version */
1962 {
1963 static const char * const versions[] = {"http/1.0", "http/1.1", NULL};
1964 this->prot_version = config->register_enum (config, "media.network.http_version",
1965 0, (char **)versions,
1966 _("HTTP protocol version to use"),
1967 _("Try these when there are communication problems."),
1968 10, prot_version_change_cb, this);
1969 }
1970
1971 /* head dump file */
1972 this->head_dump_name = config->register_string (config, "media.network.http_head_dump_file",
1973 "",
1974 _("Dump HTTP request and response heads to this file"),
1975 _("Set this for debugging."),
1976 20, head_dump_name_change_cb, this);
1977
1978 return this;
1979 }
1980