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