]> git.sesse.net Git - vlc/blob - modules/access/http.c
v4l2: missing white space
[vlc] / modules / access / http.c
1 /*****************************************************************************
2  * http.c: HTTP input module
3  *****************************************************************************
4  * Copyright (C) 2001-2008 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          RĂ©mi Denis-Courmont <rem # videolan.org>
10  *          Antoine Cellerier <dionoea at videolan dot org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36
37
38 #include <vlc_access.h>
39
40 #include <vlc_dialog.h>
41 #include <vlc_meta.h>
42 #include <vlc_network.h>
43 #include <vlc_url.h>
44 #include <vlc_tls.h>
45 #include <vlc_strings.h>
46 #include <vlc_charset.h>
47 #include <vlc_input.h>
48 #include <vlc_md5.h>
49 #include <vlc_http.h>
50
51 #ifdef HAVE_ZLIB_H
52 #   include <zlib.h>
53 #endif
54
55 #include <assert.h>
56 #include <limits.h>
57
58 /*****************************************************************************
59  * Module descriptor
60  *****************************************************************************/
61 static int  Open ( vlc_object_t * );
62 static void Close( vlc_object_t * );
63
64 #define PROXY_TEXT N_("HTTP proxy")
65 #define PROXY_LONGTEXT N_( \
66     "HTTP proxy to be used It must be of the form " \
67     "http://[user@]myproxy.mydomain:myport/ ; " \
68     "if empty, the http_proxy environment variable will be tried." )
69
70 #define PROXY_PASS_TEXT N_("HTTP proxy password")
71 #define PROXY_PASS_LONGTEXT N_( \
72     "If your HTTP proxy requires a password, set it here." )
73
74 #define RECONNECT_TEXT N_("Auto re-connect")
75 #define RECONNECT_LONGTEXT N_( \
76     "Automatically try to reconnect to the stream in case of a sudden " \
77     "disconnect." )
78
79 #define CONTINUOUS_TEXT N_("Continuous stream")
80 #define CONTINUOUS_LONGTEXT N_("Read a file that is " \
81     "being constantly updated (for example, a JPG file on a server). " \
82     "You should not globally enable this option as it will break all other " \
83     "types of HTTP streams." )
84
85 #define FORWARD_COOKIES_TEXT N_("Forward Cookies")
86 #define FORWARD_COOKIES_LONGTEXT N_("Forward Cookies across http redirections.")
87
88 #define REFERER_TEXT N_("HTTP referer value")
89 #define REFERER_LONGTEXT N_("Customize the HTTP referer, simulating a previous document")
90
91 #define UA_TEXT N_("User Agent")
92 #define UA_LONGTEXT N_("The name and version of the program will be " \
93     "provided to the HTTP server. They must be separated by a forward " \
94     "slash, e.g. FooBar/1.2.3. This option can only be specified per input " \
95     "item, not globally.")
96
97 vlc_module_begin ()
98     set_description( N_("HTTP input") )
99     set_capability( "access", 0 )
100     set_shortname( N_( "HTTP(S)" ) )
101     set_category( CAT_INPUT )
102     set_subcategory( SUBCAT_INPUT_ACCESS )
103
104     add_string( "http-proxy", NULL, PROXY_TEXT, PROXY_LONGTEXT,
105                 false )
106     add_password( "http-proxy-pwd", NULL,
107                   PROXY_PASS_TEXT, PROXY_PASS_LONGTEXT, false )
108     add_obsolete_bool( "http-use-IE-proxy" )
109     add_string( "http-referrer", NULL, REFERER_TEXT, REFERER_LONGTEXT, false )
110         change_safe()
111     add_string( "http-user-agent", NULL, UA_TEXT, UA_LONGTEXT, false )
112         change_safe()
113         change_private()
114     add_bool( "http-reconnect", false, RECONNECT_TEXT,
115               RECONNECT_LONGTEXT, true )
116     add_bool( "http-continuous", false, CONTINUOUS_TEXT,
117               CONTINUOUS_LONGTEXT, true )
118         change_safe()
119     add_bool( "http-forward-cookies", true, FORWARD_COOKIES_TEXT,
120               FORWARD_COOKIES_LONGTEXT, true )
121     /* 'itpc' = iTunes Podcast */
122     add_shortcut( "http", "https", "unsv", "itpc", "icyx" )
123     set_callbacks( Open, Close )
124 vlc_module_end ()
125
126 /*****************************************************************************
127  * Local prototypes
128  *****************************************************************************/
129
130 struct access_sys_t
131 {
132     int fd;
133     bool b_error;
134     vlc_tls_creds_t *p_creds;
135     vlc_tls_t *p_tls;
136     v_socket_t *p_vs;
137
138     /* From uri */
139     vlc_url_t url;
140     char    *psz_user_agent;
141     char    *psz_referrer;
142     http_auth_t auth;
143
144     /* Proxy */
145     bool b_proxy;
146     vlc_url_t  proxy;
147     http_auth_t proxy_auth;
148     char       *psz_proxy_passbuf;
149
150     /* */
151     int        i_code;
152     const char *psz_protocol;
153     int        i_version;
154
155     char       *psz_mime;
156     char       *psz_pragma;
157     char       *psz_location;
158     bool b_mms;
159     bool b_icecast;
160 #ifdef HAVE_ZLIB_H
161     bool b_compressed;
162     struct
163     {
164         z_stream   stream;
165         uint8_t   *p_buffer;
166     } inflate;
167 #endif
168
169     bool b_chunked;
170     int64_t    i_chunk;
171
172     int        i_icy_meta;
173     uint64_t   i_icy_offset;
174     char       *psz_icy_name;
175     char       *psz_icy_genre;
176     char       *psz_icy_title;
177
178     uint64_t i_remaining;
179
180     bool b_seekable;
181     bool b_reconnect;
182     bool b_continuous;
183     bool b_pace_control;
184     bool b_persist;
185     bool b_has_size;
186
187     vlc_array_t * cookies;
188 };
189
190 /* */
191 static int OpenWithCookies( vlc_object_t *p_this, const char *psz_access,
192                             unsigned i_redirect, vlc_array_t *cookies );
193
194 /* */
195 static ssize_t Read( access_t *, uint8_t *, size_t );
196 static ssize_t ReadCompressed( access_t *, uint8_t *, size_t );
197 static int Seek( access_t *, uint64_t );
198 static int Control( access_t *, int, va_list );
199
200 /* */
201 static int Connect( access_t *, uint64_t );
202 static int Request( access_t *p_access, uint64_t i_tell );
203 static void Disconnect( access_t * );
204
205 /* Small Cookie utilities. Cookies support is partial. */
206 static char * cookie_get_content( const char * cookie );
207 static char * cookie_get_domain( const char * cookie );
208 static char * cookie_get_name( const char * cookie );
209 static void cookie_append( vlc_array_t * cookies, char * cookie );
210
211
212 static void AuthReply( access_t *p_acces, const char *psz_prefix,
213                        vlc_url_t *p_url, http_auth_t *p_auth );
214 static int AuthCheckReply( access_t *p_access, const char *psz_header,
215                            vlc_url_t *p_url, http_auth_t *p_auth );
216
217 /*****************************************************************************
218  * Open:
219  *****************************************************************************/
220 static int Open( vlc_object_t *p_this )
221 {
222     access_t *p_access = (access_t*)p_this;
223     return OpenWithCookies( p_this, p_access->psz_access, 5, NULL );
224 }
225
226 /**
227  * Open the given url using the given cookies
228  * @param p_this: the vlc object
229  * @psz_access: the acces to use (http, https, ...) (this value must be used
230  *              instead of p_access->psz_access)
231  * @i_redirect: number of redirections remaining
232  * @cookies: the available cookies
233  * @return vlc error codes
234  */
235 static int OpenWithCookies( vlc_object_t *p_this, const char *psz_access,
236                             unsigned i_redirect, vlc_array_t *cookies )
237 {
238     access_t     *p_access = (access_t*)p_this;
239     access_sys_t *p_sys;
240     char         *psz, *p;
241
242     /* Only forward an store cookies if the corresponding option is activated */
243     bool   b_forward_cookies = var_InheritBool( p_access, "http-forward-cookies" );
244     vlc_array_t * saved_cookies = b_forward_cookies ? (cookies ? cookies : vlc_array_new()) : NULL;
245
246     /* Set up p_access */
247     STANDARD_READ_ACCESS_INIT;
248 #ifdef HAVE_ZLIB_H
249     p_access->pf_read = ReadCompressed;
250 #endif
251     p_sys->fd = -1;
252     p_sys->b_proxy = false;
253     p_sys->psz_proxy_passbuf = NULL;
254     p_sys->i_version = 1;
255     p_sys->b_seekable = true;
256     p_sys->psz_mime = NULL;
257     p_sys->psz_pragma = NULL;
258     p_sys->b_mms = false;
259     p_sys->b_icecast = false;
260     p_sys->psz_location = NULL;
261     p_sys->psz_user_agent = NULL;
262     p_sys->psz_referrer = NULL;
263     p_sys->b_pace_control = true;
264 #ifdef HAVE_ZLIB_H
265     p_sys->b_compressed = false;
266     /* 15 is the max windowBits, +32 to enable optional gzip decoding */
267     if( inflateInit2( &p_sys->inflate.stream, 32+15 ) != Z_OK )
268         msg_Warn( p_access, "Error during zlib initialisation: %s",
269                   p_sys->inflate.stream.msg );
270     if( zlibCompileFlags() & (1<<17) )
271         msg_Warn( p_access, "Your zlib was compiled without gzip support." );
272     p_sys->inflate.p_buffer = NULL;
273 #endif
274     p_sys->p_tls = NULL;
275     p_sys->p_vs = NULL;
276     p_sys->i_icy_meta = 0;
277     p_sys->i_icy_offset = 0;
278     p_sys->psz_icy_name = NULL;
279     p_sys->psz_icy_genre = NULL;
280     p_sys->psz_icy_title = NULL;
281     p_sys->i_remaining = 0;
282     p_sys->b_persist = false;
283     p_sys->b_has_size = false;
284     p_access->info.i_size = 0;
285     p_access->info.i_pos  = 0;
286     p_access->info.b_eof  = false;
287
288     p_sys->cookies = saved_cookies;
289
290     http_auth_Init( &p_sys->auth );
291     http_auth_Init( &p_sys->proxy_auth );
292
293     /* Parse URI - remove spaces */
294     p = psz = strdup( p_access->psz_location );
295     while( (p = strchr( p, ' ' )) != NULL )
296         *p = '+';
297     vlc_UrlParse( &p_sys->url, psz, 0 );
298     free( psz );
299
300     if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
301     {
302         msg_Warn( p_access, "invalid host" );
303         goto error;
304     }
305     if( !strncmp( psz_access, "https", 5 ) )
306     {
307         /* HTTP over SSL */
308         p_sys->p_creds = vlc_tls_ClientCreate( p_this );
309         if( p_sys->p_creds == NULL )
310             goto error;
311         if( p_sys->url.i_port <= 0 )
312             p_sys->url.i_port = 443;
313     }
314     else
315     {
316         if( p_sys->url.i_port <= 0 )
317             p_sys->url.i_port = 80;
318     }
319
320     /* Determine the HTTP user agent */
321     /* See RFC2616 Â§2.2 token and comment definition, and Â§3.8 and
322      * Â§14.43 user-agent header */
323     p_sys->psz_user_agent = var_InheritString( p_access, "http-user-agent" );
324     if (p_sys->psz_user_agent)
325     {
326         unsigned comment_level = 0;
327         for( char *p = p_sys->psz_user_agent; *p; p++ )
328         {
329             uint8_t c = *p;
330             if (comment_level == 0)
331             {
332                 if( c < 32 || strchr( ")<>@,;:\\\"[]?={}", c ) )
333                     *p = '_'; /* remove potentially harmful characters */
334             }
335             else
336             {
337                 if (c == ')')
338                     comment_level--;
339                 else if( c < 32 && strchr( "\t\r\n", c ) == NULL)
340                     *p = '_'; /* remove potentially harmful characters */
341             }
342             if (c == '(')
343             {
344                 if (comment_level == UINT_MAX)
345                     break;
346                 comment_level++;
347             }
348         }
349         /* truncate evil unclosed comments */
350         if (comment_level > 0)
351         {
352             char *p = strchr(p_sys->psz_user_agent, '(');
353             *p = '\0';
354         }
355     }
356
357     /* HTTP referrer */
358     p_sys->psz_referrer = var_InheritString( p_access, "http-referrer" );
359
360     /* Check proxy */
361     psz = var_InheritString( p_access, "http-proxy" );
362     if( psz == NULL )
363     {
364         char *url;
365
366         if (likely(asprintf(&url, "%s://%s", psz_access,
367                             p_access->psz_location) != -1))
368         {
369             msg_Dbg(p_access, "querying proxy for %s", url);
370             psz = vlc_getProxyUrl(url);
371             free(url);
372         }
373
374         if (psz != NULL)
375             msg_Dbg(p_access, "proxy: %s", psz);
376         else
377             msg_Dbg(p_access, "no proxy");
378     }
379     if( psz != NULL )
380     {
381         p_sys->b_proxy = true;
382         vlc_UrlParse( &p_sys->proxy, psz, 0 );
383         free( psz );
384
385         psz = var_InheritString( p_access, "http-proxy-pwd" );
386         if( psz )
387             p_sys->proxy.psz_password = p_sys->psz_proxy_passbuf = psz;
388
389         if( p_sys->proxy.psz_host == NULL || *p_sys->proxy.psz_host == '\0' )
390         {
391             msg_Warn( p_access, "invalid proxy host" );
392             goto error;
393         }
394         if( p_sys->proxy.i_port <= 0 )
395         {
396             p_sys->proxy.i_port = 80;
397         }
398     }
399
400     msg_Dbg( p_access, "http: server='%s' port=%d file='%s'",
401              p_sys->url.psz_host, p_sys->url.i_port,
402              p_sys->url.psz_path != NULL ? p_sys->url.psz_path : "" );
403     if( p_sys->b_proxy )
404     {
405         msg_Dbg( p_access, "      proxy %s:%d", p_sys->proxy.psz_host,
406                  p_sys->proxy.i_port );
407     }
408     if( p_sys->url.psz_username && *p_sys->url.psz_username )
409     {
410         msg_Dbg( p_access, "      user='%s'", p_sys->url.psz_username );
411     }
412
413     p_sys->b_reconnect = var_InheritBool( p_access, "http-reconnect" );
414     p_sys->b_continuous = var_InheritBool( p_access, "http-continuous" );
415
416 connect:
417     /* Connect */
418     switch( Connect( p_access, 0 ) )
419     {
420         case -1:
421             goto error;
422
423         case -2:
424             /* Retry with http 1.0 */
425             msg_Dbg( p_access, "switching to HTTP version 1.0" );
426             p_sys->i_version = 0;
427             p_sys->b_seekable = false;
428
429             if( !vlc_object_alive (p_access) || Connect( p_access, 0 ) )
430                 goto error;
431
432         case 0:
433             break;
434
435         default:
436             assert(0);
437     }
438
439     if( p_sys->i_code == 401 )
440     {
441         if( p_sys->auth.psz_realm == NULL )
442         {
443             msg_Err( p_access, "authentication failed without realm" );
444             goto error;
445         }
446         char *psz_login, *psz_password;
447         /* FIXME ? */
448         if( p_sys->url.psz_username && p_sys->url.psz_password &&
449             p_sys->auth.psz_nonce && p_sys->auth.i_nonce == 0 )
450         {
451             Disconnect( p_access );
452             goto connect;
453         }
454         msg_Dbg( p_access, "authentication failed for realm %s",
455                  p_sys->auth.psz_realm );
456         dialog_Login( p_access, &psz_login, &psz_password,
457                       _("HTTP authentication"),
458              _("Please enter a valid login name and a password for realm %s."),
459                       p_sys->auth.psz_realm );
460         if( psz_login != NULL && psz_password != NULL )
461         {
462             msg_Dbg( p_access, "retrying with user=%s", psz_login );
463             p_sys->url.psz_username = psz_login;
464             p_sys->url.psz_password = psz_password;
465             Disconnect( p_access );
466             goto connect;
467         }
468         else
469         {
470             free( psz_login );
471             free( psz_password );
472             goto error;
473         }
474     }
475
476     if( ( p_sys->i_code == 301 || p_sys->i_code == 302 ||
477           p_sys->i_code == 303 || p_sys->i_code == 307 ) &&
478         p_sys->psz_location && *p_sys->psz_location )
479     {
480         msg_Dbg( p_access, "redirection to %s", p_sys->psz_location );
481
482         /* Check the number of redirection already done */
483         if( i_redirect == 0 )
484         {
485             msg_Err( p_access, "Too many redirection: break potential infinite"
486                      "loop" );
487             goto error;
488         }
489
490         const char *psz_protocol;
491         if( !strncmp( p_sys->psz_location, "http://", 7 ) )
492             psz_protocol = "http";
493         else if( !strncmp( p_sys->psz_location, "https://", 8 ) )
494             psz_protocol = "https";
495         else
496         {   /* Do not accept redirection outside of HTTP */
497             msg_Err( p_access, "unsupported redirection ignored" );
498             goto error;
499         }
500         free( p_access->psz_location );
501         p_access->psz_location = strdup( p_sys->psz_location
502                                        + strlen( psz_protocol ) + 3 );
503         /* Clean up current Open() run */
504         vlc_UrlClean( &p_sys->url );
505         http_auth_Reset( &p_sys->auth );
506         vlc_UrlClean( &p_sys->proxy );
507         free( p_sys->psz_proxy_passbuf );
508         http_auth_Reset( &p_sys->proxy_auth );
509         free( p_sys->psz_mime );
510         free( p_sys->psz_pragma );
511         free( p_sys->psz_location );
512         free( p_sys->psz_user_agent );
513         free( p_sys->psz_referrer );
514
515         Disconnect( p_access );
516         vlc_tls_Delete( p_sys->p_creds );
517         cookies = p_sys->cookies;
518 #ifdef HAVE_ZLIB_H
519         inflateEnd( &p_sys->inflate.stream );
520 #endif
521         free( p_sys );
522
523         /* Do new Open() run with new data */
524         return OpenWithCookies( p_this, psz_protocol, i_redirect - 1,
525                                 cookies );
526     }
527
528     if( p_sys->b_mms )
529     {
530         msg_Dbg( p_access, "this is actually a live mms server, BAIL" );
531         goto error;
532     }
533
534     if( !strcmp( p_sys->psz_protocol, "ICY" ) || p_sys->b_icecast )
535     {
536         if( p_sys->psz_mime && strcasecmp( p_sys->psz_mime, "application/ogg" ) )
537         {
538             if( !strcasecmp( p_sys->psz_mime, "video/nsv" ) ||
539                 !strcasecmp( p_sys->psz_mime, "video/nsa" ) )
540             {
541                 free( p_access->psz_demux );
542                 p_access->psz_demux = strdup( "nsv" );
543             }
544             else if( !strcasecmp( p_sys->psz_mime, "audio/aac" ) ||
545                      !strcasecmp( p_sys->psz_mime, "audio/aacp" ) )
546             {
547                 free( p_access->psz_demux );
548                 p_access->psz_demux = strdup( "m4a" );
549             }
550             else if( !strcasecmp( p_sys->psz_mime, "audio/mpeg" ) )
551             {
552                 free( p_access->psz_demux );
553                 p_access->psz_demux = strdup( "mp3" );
554             }
555
556             msg_Info( p_access, "Raw-audio server found, %s demuxer selected",
557                       p_access->psz_demux );
558
559 #if 0       /* Doesn't work really well because of the pre-buffering in
560              * shoutcast servers (the buffer content will be sent as fast as
561              * possible). */
562             p_sys->b_pace_control = false;
563 #endif
564         }
565         else if( !p_sys->psz_mime )
566         {
567             free( p_access->psz_demux );
568             /* Shoutcast */
569             p_access->psz_demux = strdup( "mp3" );
570         }
571         /* else probably Ogg Vorbis */
572     }
573     else if( !strcasecmp( psz_access, "unsv" ) &&
574              p_sys->psz_mime &&
575              !strcasecmp( p_sys->psz_mime, "misc/ultravox" ) )
576     {
577         free( p_access->psz_demux );
578         /* Grrrr! detect ultravox server and force NSV demuxer */
579         p_access->psz_demux = strdup( "nsv" );
580     }
581     else if( !strcmp( psz_access, "itpc" ) )
582     {
583         free( p_access->psz_demux );
584         p_access->psz_demux = strdup( "podcast" );
585     }
586     else if( p_sys->psz_mime &&
587              !strncasecmp( p_sys->psz_mime, "application/xspf+xml", 20 ) &&
588              ( memchr( " ;\t", p_sys->psz_mime[20], 4 ) != NULL ) )
589     {
590         free( p_access->psz_demux );
591         p_access->psz_demux = strdup( "xspf-open" );
592     }
593
594     if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );
595
596     return VLC_SUCCESS;
597
598 error:
599     vlc_UrlClean( &p_sys->url );
600     vlc_UrlClean( &p_sys->proxy );
601     free( p_sys->psz_proxy_passbuf );
602     free( p_sys->psz_mime );
603     free( p_sys->psz_pragma );
604     free( p_sys->psz_location );
605     free( p_sys->psz_user_agent );
606     free( p_sys->psz_referrer );
607
608     Disconnect( p_access );
609     vlc_tls_Delete( p_sys->p_creds );
610
611     if( p_sys->cookies )
612     {
613         int i;
614         for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
615             free(vlc_array_item_at_index( p_sys->cookies, i ));
616         vlc_array_destroy( p_sys->cookies );
617     }
618
619 #ifdef HAVE_ZLIB_H
620     inflateEnd( &p_sys->inflate.stream );
621 #endif
622     free( p_sys );
623     return VLC_EGENERIC;
624 }
625
626 /*****************************************************************************
627  * Close:
628  *****************************************************************************/
629 static void Close( vlc_object_t *p_this )
630 {
631     access_t     *p_access = (access_t*)p_this;
632     access_sys_t *p_sys = p_access->p_sys;
633
634     vlc_UrlClean( &p_sys->url );
635     http_auth_Reset( &p_sys->auth );
636     vlc_UrlClean( &p_sys->proxy );
637     http_auth_Reset( &p_sys->proxy_auth );
638
639     free( p_sys->psz_mime );
640     free( p_sys->psz_pragma );
641     free( p_sys->psz_location );
642
643     free( p_sys->psz_icy_name );
644     free( p_sys->psz_icy_genre );
645     free( p_sys->psz_icy_title );
646
647     free( p_sys->psz_user_agent );
648     free( p_sys->psz_referrer );
649
650     Disconnect( p_access );
651     vlc_tls_Delete( p_sys->p_creds );
652
653     if( p_sys->cookies )
654     {
655         int i;
656         for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
657             free(vlc_array_item_at_index( p_sys->cookies, i ));
658         vlc_array_destroy( p_sys->cookies );
659     }
660
661 #ifdef HAVE_ZLIB_H
662     inflateEnd( &p_sys->inflate.stream );
663     free( p_sys->inflate.p_buffer );
664 #endif
665
666     free( p_sys );
667 }
668
669 /* Read data from the socket taking care of chunked transfer if needed */
670 static int ReadData( access_t *p_access, int *pi_read,
671                      uint8_t *p_buffer, size_t i_len )
672 {
673     access_sys_t *p_sys = p_access->p_sys;
674     if( p_sys->b_chunked )
675     {
676         if( p_sys->i_chunk < 0 )
677             return VLC_EGENERIC;
678
679         if( p_sys->i_chunk <= 0 )
680         {
681             char *psz = net_Gets( p_access, p_sys->fd, p_sys->p_vs );
682             /* read the chunk header */
683             if( psz == NULL )
684             {
685                 /* fatal error - end of file */
686                 msg_Dbg( p_access, "failed reading chunk-header line" );
687                 return VLC_EGENERIC;
688             }
689             p_sys->i_chunk = strtoll( psz, NULL, 16 );
690             free( psz );
691
692             if( p_sys->i_chunk <= 0 )   /* eof */
693             {
694                 p_sys->i_chunk = -1;
695                 return VLC_EGENERIC;
696             }
697         }
698
699         if( i_len > p_sys->i_chunk )
700             i_len = p_sys->i_chunk;
701     }
702     *pi_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, p_buffer, i_len, false );
703     if( *pi_read <= 0 )
704         return VLC_SUCCESS;
705
706     if( p_sys->b_chunked )
707     {
708         p_sys->i_chunk -= *pi_read;
709         if( p_sys->i_chunk <= 0 )
710         {
711             /* read the empty line */
712             char *psz = net_Gets( p_access, p_sys->fd, p_sys->p_vs );
713             free( psz );
714         }
715     }
716     return VLC_SUCCESS;
717 }
718
719 /*****************************************************************************
720  * Read: Read up to i_len bytes from the http connection and place in
721  * p_buffer. Return the actual number of bytes read
722  *****************************************************************************/
723 static int ReadICYMeta( access_t *p_access );
724 static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
725 {
726     access_sys_t *p_sys = p_access->p_sys;
727     int i_read;
728
729     if( p_sys->fd == -1 )
730         goto fatal;
731
732     if( p_sys->b_has_size )
733     {
734         /* Remaining bytes in the file */
735         uint64_t remainder = p_access->info.i_size - p_access->info.i_pos;
736         if( remainder < i_len )
737             i_len = remainder;
738
739         /* Remaining bytes in the response */
740         if( p_sys->i_remaining < i_len )
741             i_len = p_sys->i_remaining;
742     }
743     if( i_len == 0 )
744         goto fatal;
745
746     if( p_sys->i_icy_meta > 0 && p_access->info.i_pos - p_sys->i_icy_offset > 0 )
747     {
748         int64_t i_next = p_sys->i_icy_meta -
749                                     (p_access->info.i_pos - p_sys->i_icy_offset ) % p_sys->i_icy_meta;
750
751         if( i_next == p_sys->i_icy_meta )
752         {
753             if( ReadICYMeta( p_access ) )
754                 goto fatal;
755         }
756         if( i_len > i_next )
757             i_len = i_next;
758     }
759
760     if( ReadData( p_access, &i_read, p_buffer, i_len ) )
761         goto fatal;
762
763     if( i_read <= 0 )
764     {
765         /*
766          * I very much doubt that this will work.
767          * If i_read == 0, the connection *IS* dead, so the only
768          * sensible thing to do is Disconnect() and then retry.
769          * Otherwise, I got recv() completely wrong. -- Courmisch
770          */
771         if( p_sys->b_continuous )
772         {
773             Request( p_access, 0 );
774             p_sys->b_continuous = false;
775             i_read = Read( p_access, p_buffer, i_len );
776             p_sys->b_continuous = true;
777         }
778         Disconnect( p_access );
779         if( p_sys->b_reconnect && vlc_object_alive( p_access ) )
780         {
781             msg_Dbg( p_access, "got disconnected, trying to reconnect" );
782             if( Connect( p_access, p_access->info.i_pos ) )
783             {
784                 msg_Dbg( p_access, "reconnection failed" );
785             }
786             else
787             {
788                 p_sys->b_reconnect = false;
789                 i_read = Read( p_access, p_buffer, i_len );
790                 p_sys->b_reconnect = true;
791
792                 return i_read;
793             }
794         }
795
796         if( i_read <= 0 )
797         {
798             if( i_read < 0 )
799                 p_sys->b_error = true;
800             goto fatal;
801         }
802     }
803
804     assert( i_read >= 0 );
805     p_access->info.i_pos += i_read;
806     if( p_sys->b_has_size )
807     {
808         assert( p_access->info.i_pos <= p_access->info.i_size );
809         assert( (unsigned)i_read <= p_sys->i_remaining );
810         p_sys->i_remaining -= i_read;
811     }
812
813     return i_read;
814
815 fatal:
816     p_access->info.b_eof = true;
817     return 0;
818 }
819
820 static int ReadICYMeta( access_t *p_access )
821 {
822     access_sys_t *p_sys = p_access->p_sys;
823
824     uint8_t buffer;
825     char *p, *psz_meta;
826     int i_read;
827
828     /* Read meta data length */
829     if( ReadData( p_access, &i_read, &buffer, 1 ) )
830         return VLC_EGENERIC;
831     if( i_read != 1 )
832         return VLC_EGENERIC;
833     const int i_size = buffer << 4;
834     /* msg_Dbg( p_access, "ICY meta size=%u", i_size); */
835
836     psz_meta = malloc( i_size + 1 );
837     for( i_read = 0; i_read < i_size; )
838     {
839         int i_tmp;
840         if( ReadData( p_access, &i_tmp, (uint8_t *)&psz_meta[i_read], i_size - i_read ) || i_tmp <= 0 )
841         {
842             free( psz_meta );
843             return VLC_EGENERIC;
844         }
845         i_read += i_tmp;
846     }
847     psz_meta[i_read] = '\0'; /* Just in case */
848
849     /* msg_Dbg( p_access, "icy-meta=%s", psz_meta ); */
850
851     /* Now parse the meta */
852     /* Look for StreamTitle= */
853     p = strcasestr( (char *)psz_meta, "StreamTitle=" );
854     if( p )
855     {
856         p += strlen( "StreamTitle=" );
857         if( *p == '\'' || *p == '"' )
858         {
859             char closing[] = { p[0], ';', '\0' };
860             char *psz = strstr( &p[1], closing );
861             if( !psz )
862                 psz = strchr( &p[1], ';' );
863
864             if( psz ) *psz = '\0';
865         }
866         else
867         {
868             char *psz = strchr( &p[1], ';' );
869             if( psz ) *psz = '\0';
870         }
871
872         if( !p_sys->psz_icy_title ||
873             strcmp( p_sys->psz_icy_title, &p[1] ) )
874         {
875             free( p_sys->psz_icy_title );
876             char *psz_tmp = strdup( &p[1] );
877             p_sys->psz_icy_title = EnsureUTF8( psz_tmp );
878             if( !p_sys->psz_icy_title )
879                 free( psz_tmp );
880             p_access->info.i_update |= INPUT_UPDATE_META;
881
882             msg_Dbg( p_access, "New Title=%s", p_sys->psz_icy_title );
883         }
884     }
885     free( psz_meta );
886
887     return VLC_SUCCESS;
888 }
889
890 #ifdef HAVE_ZLIB_H
891 static ssize_t ReadCompressed( access_t *p_access, uint8_t *p_buffer,
892                                size_t i_len )
893 {
894     access_sys_t *p_sys = p_access->p_sys;
895
896     if( p_sys->b_compressed )
897     {
898         int i_ret;
899
900         if( !p_sys->inflate.p_buffer )
901             p_sys->inflate.p_buffer = malloc( 256 * 1024 );
902
903         if( p_sys->inflate.stream.avail_in == 0 )
904         {
905             ssize_t i_read = Read( p_access, p_sys->inflate.p_buffer, 256 * 1024 );
906             if( i_read <= 0 ) return i_read;
907             p_sys->inflate.stream.next_in = p_sys->inflate.p_buffer;
908             p_sys->inflate.stream.avail_in = i_read;
909         }
910
911         p_sys->inflate.stream.avail_out = i_len;
912         p_sys->inflate.stream.next_out = p_buffer;
913
914         i_ret = inflate( &p_sys->inflate.stream, Z_SYNC_FLUSH );
915         if ( i_ret != Z_OK && i_ret != Z_STREAM_END )
916             msg_Warn( p_access, "inflate return value: %d, %s", i_ret, p_sys->inflate.stream.msg );
917
918         return i_len - p_sys->inflate.stream.avail_out;
919     }
920     else
921     {
922         return Read( p_access, p_buffer, i_len );
923     }
924 }
925 #endif
926
927 /*****************************************************************************
928  * Seek: close and re-open a connection at the right place
929  *****************************************************************************/
930 static int Seek( access_t *p_access, uint64_t i_pos )
931 {
932     msg_Dbg( p_access, "trying to seek to %"PRId64, i_pos );
933
934     Disconnect( p_access );
935
936     if( p_access->info.i_size
937      && i_pos >= p_access->info.i_size ) {
938         msg_Err( p_access, "seek to far" );
939         int retval = Seek( p_access, p_access->info.i_size - 1 );
940         if( retval == VLC_SUCCESS ) {
941             uint8_t p_buffer[2];
942             Read( p_access, p_buffer, 1);
943             p_access->info.b_eof  = false;
944         }
945         return retval;
946     }
947     if( Connect( p_access, i_pos ) )
948     {
949         msg_Err( p_access, "seek failed" );
950         p_access->info.b_eof = true;
951         return VLC_EGENERIC;
952     }
953     return VLC_SUCCESS;
954 }
955
956 /*****************************************************************************
957  * Control:
958  *****************************************************************************/
959 static int Control( access_t *p_access, int i_query, va_list args )
960 {
961     access_sys_t *p_sys = p_access->p_sys;
962     bool       *pb_bool;
963     int64_t    *pi_64;
964     vlc_meta_t *p_meta;
965
966     switch( i_query )
967     {
968         /* */
969         case ACCESS_CAN_SEEK:
970             pb_bool = (bool*)va_arg( args, bool* );
971             *pb_bool = p_sys->b_seekable;
972             break;
973         case ACCESS_CAN_FASTSEEK:
974             pb_bool = (bool*)va_arg( args, bool* );
975             *pb_bool = false;
976             break;
977         case ACCESS_CAN_PAUSE:
978         case ACCESS_CAN_CONTROL_PACE:
979             pb_bool = (bool*)va_arg( args, bool* );
980
981 #if 0       /* Disable for now until we have a clock synchro algo
982              * which works with something else than MPEG over UDP */
983             *pb_bool = p_sys->b_pace_control;
984 #endif
985             *pb_bool = true;
986             break;
987
988         /* */
989         case ACCESS_GET_PTS_DELAY:
990             pi_64 = (int64_t*)va_arg( args, int64_t * );
991             *pi_64 = INT64_C(1000)
992                 * var_InheritInteger( p_access, "network-caching" );
993             break;
994
995         /* */
996         case ACCESS_SET_PAUSE_STATE:
997             break;
998
999         case ACCESS_GET_META:
1000             p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
1001
1002             if( p_sys->psz_icy_name )
1003                 vlc_meta_Set( p_meta, vlc_meta_Title, p_sys->psz_icy_name );
1004             if( p_sys->psz_icy_genre )
1005                 vlc_meta_Set( p_meta, vlc_meta_Genre, p_sys->psz_icy_genre );
1006             if( p_sys->psz_icy_title )
1007                 vlc_meta_Set( p_meta, vlc_meta_NowPlaying, p_sys->psz_icy_title );
1008             break;
1009
1010         case ACCESS_GET_CONTENT_TYPE:
1011             *va_arg( args, char ** ) =
1012                 p_sys->psz_mime ? strdup( p_sys->psz_mime ) : NULL;
1013             break;
1014
1015         case ACCESS_GET_TITLE_INFO:
1016         case ACCESS_SET_TITLE:
1017         case ACCESS_SET_SEEKPOINT:
1018         case ACCESS_SET_PRIVATE_ID_STATE:
1019             return VLC_EGENERIC;
1020
1021         default:
1022             msg_Warn( p_access, "unimplemented query in control" );
1023             return VLC_EGENERIC;
1024
1025     }
1026     return VLC_SUCCESS;
1027 }
1028
1029 /*****************************************************************************
1030  * Connect:
1031  *****************************************************************************/
1032 static int Connect( access_t *p_access, uint64_t i_tell )
1033 {
1034     access_sys_t   *p_sys = p_access->p_sys;
1035     vlc_url_t      srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url;
1036
1037     /* Clean info */
1038     free( p_sys->psz_location );
1039     free( p_sys->psz_mime );
1040     free( p_sys->psz_pragma );
1041
1042     free( p_sys->psz_icy_genre );
1043     free( p_sys->psz_icy_name );
1044     free( p_sys->psz_icy_title );
1045
1046
1047     p_sys->psz_location = NULL;
1048     p_sys->psz_mime = NULL;
1049     p_sys->psz_pragma = NULL;
1050     p_sys->b_mms = false;
1051     p_sys->b_chunked = false;
1052     p_sys->i_chunk = 0;
1053     p_sys->i_icy_meta = 0;
1054     p_sys->i_icy_offset = i_tell;
1055     p_sys->psz_icy_name = NULL;
1056     p_sys->psz_icy_genre = NULL;
1057     p_sys->psz_icy_title = NULL;
1058     p_sys->i_remaining = 0;
1059     p_sys->b_persist = false;
1060     p_sys->b_has_size = false;
1061     p_access->info.i_size = 0;
1062     p_access->info.i_pos  = i_tell;
1063     p_access->info.b_eof  = false;
1064
1065     /* Open connection */
1066     assert( p_sys->fd == -1 ); /* No open sockets (leaking fds is BAD) */
1067     p_sys->fd = net_ConnectTCP( p_access, srv.psz_host, srv.i_port );
1068     if( p_sys->fd == -1 )
1069     {
1070         msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port );
1071         return -1;
1072     }
1073     setsockopt (p_sys->fd, SOL_SOCKET, SO_KEEPALIVE, &(int){ 1 }, sizeof (int));
1074
1075     /* Initialize TLS/SSL session */
1076     if( p_sys->p_creds != NULL )
1077     {
1078         /* CONNECT to establish TLS tunnel through HTTP proxy */
1079         if( p_sys->b_proxy )
1080         {
1081             char *psz;
1082             unsigned i_status = 0;
1083
1084             if( p_sys->i_version == 0 )
1085             {
1086                 /* CONNECT is not in HTTP/1.0 */
1087                 Disconnect( p_access );
1088                 return -1;
1089             }
1090
1091             net_Printf( p_access, p_sys->fd, NULL,
1092                         "CONNECT %s:%d HTTP/1.%d\r\nHost: %s:%d\r\n\r\n",
1093                         p_sys->url.psz_host, p_sys->url.i_port,
1094                         p_sys->i_version,
1095                         p_sys->url.psz_host, p_sys->url.i_port);
1096
1097             psz = net_Gets( p_access, p_sys->fd, NULL );
1098             if( psz == NULL )
1099             {
1100                 msg_Err( p_access, "cannot establish HTTP/TLS tunnel" );
1101                 Disconnect( p_access );
1102                 return -1;
1103             }
1104
1105             sscanf( psz, "HTTP/%*u.%*u %3u", &i_status );
1106             free( psz );
1107
1108             if( ( i_status / 100 ) != 2 )
1109             {
1110                 msg_Err( p_access, "HTTP/TLS tunnel through proxy denied" );
1111                 Disconnect( p_access );
1112                 return -1;
1113             }
1114
1115             do
1116             {
1117                 psz = net_Gets( p_access, p_sys->fd, NULL );
1118                 if( psz == NULL )
1119                 {
1120                     msg_Err( p_access, "HTTP proxy connection failed" );
1121                     Disconnect( p_access );
1122                     return -1;
1123                 }
1124
1125                 if( *psz == '\0' )
1126                     i_status = 0;
1127
1128                 free( psz );
1129
1130                 if( !vlc_object_alive (p_access) || p_sys->b_error )
1131                 {
1132                     Disconnect( p_access );
1133                     return -1;
1134                 }
1135             }
1136             while( i_status );
1137         }
1138
1139         /* TLS/SSL handshake */
1140         p_sys->p_tls = vlc_tls_ClientSessionCreate( p_sys->p_creds, p_sys->fd,
1141                                                 p_sys->url.psz_host, "https" );
1142         if( p_sys->p_tls == NULL )
1143         {
1144             msg_Err( p_access, "cannot establish HTTP/TLS session" );
1145             Disconnect( p_access );
1146             return -1;
1147         }
1148         p_sys->p_vs = &p_sys->p_tls->sock;
1149     }
1150
1151     return Request( p_access, i_tell ) ? -2 : 0;
1152 }
1153
1154
1155 static int Request( access_t *p_access, uint64_t i_tell )
1156 {
1157     access_sys_t   *p_sys = p_access->p_sys;
1158     char           *psz ;
1159     v_socket_t     *pvs = p_sys->p_vs;
1160     p_sys->b_persist = false;
1161
1162     p_sys->i_remaining = 0;
1163
1164     const char *psz_path = p_sys->url.psz_path;
1165     if( !psz_path || !*psz_path )
1166         psz_path = "/";
1167     if( p_sys->b_proxy && pvs == NULL )
1168         net_Printf( p_access, p_sys->fd, NULL,
1169                     "GET http://%s:%d%s HTTP/1.%d\r\n",
1170                     p_sys->url.psz_host, p_sys->url.i_port,
1171                     psz_path, p_sys->i_version );
1172     else
1173         net_Printf( p_access, p_sys->fd, pvs, "GET %s HTTP/1.%d\r\n",
1174                     psz_path, p_sys->i_version );
1175     if( p_sys->url.i_port != (pvs ? 443 : 80) )
1176         net_Printf( p_access, p_sys->fd, pvs, "Host: %s:%d\r\n",
1177                     p_sys->url.psz_host, p_sys->url.i_port );
1178     else
1179         net_Printf( p_access, p_sys->fd, pvs, "Host: %s\r\n",
1180                     p_sys->url.psz_host );
1181     /* User Agent */
1182     net_Printf( p_access, p_sys->fd, pvs, "User-Agent: %s\r\n",
1183                 p_sys->psz_user_agent );
1184     /* Referrer */
1185     if (p_sys->psz_referrer)
1186     {
1187         net_Printf( p_access, p_sys->fd, pvs, "Referer: %s\r\n",
1188                     p_sys->psz_referrer);
1189     }
1190     /* Offset */
1191     if( p_sys->i_version == 1 && ! p_sys->b_continuous )
1192     {
1193         p_sys->b_persist = true;
1194         net_Printf( p_access, p_sys->fd, pvs,
1195                     "Range: bytes=%"PRIu64"-\r\n", i_tell );
1196         net_Printf( p_access, p_sys->fd, pvs, "Connection: close\r\n" );
1197     }
1198
1199     /* Cookies */
1200     if( p_sys->cookies )
1201     {
1202         int i;
1203         for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
1204         {
1205             const char * cookie = vlc_array_item_at_index( p_sys->cookies, i );
1206             char * psz_cookie_content = cookie_get_content( cookie );
1207             char * psz_cookie_domain = cookie_get_domain( cookie );
1208
1209             assert( psz_cookie_content );
1210
1211             /* FIXME: This is clearly not conforming to the rfc */
1212             bool is_in_right_domain = (!psz_cookie_domain || strstr( p_sys->url.psz_host, psz_cookie_domain ));
1213
1214             if( is_in_right_domain )
1215             {
1216                 msg_Dbg( p_access, "Sending Cookie %s", psz_cookie_content );
1217                 if( net_Printf( p_access, p_sys->fd, pvs, "Cookie: %s\r\n", psz_cookie_content ) < 0 )
1218                     msg_Err( p_access, "failed to send Cookie" );
1219             }
1220             free( psz_cookie_content );
1221             free( psz_cookie_domain );
1222         }
1223     }
1224
1225     /* Authentication */
1226     if( p_sys->url.psz_username || p_sys->url.psz_password )
1227         AuthReply( p_access, "", &p_sys->url, &p_sys->auth );
1228
1229     /* Proxy Authentication */
1230     if( p_sys->proxy.psz_username || p_sys->proxy.psz_password )
1231         AuthReply( p_access, "Proxy-", &p_sys->proxy, &p_sys->proxy_auth );
1232
1233     /* ICY meta data request */
1234     net_Printf( p_access, p_sys->fd, pvs, "Icy-MetaData: 1\r\n" );
1235
1236
1237     if( net_Printf( p_access, p_sys->fd, pvs, "\r\n" ) < 0 )
1238     {
1239         msg_Err( p_access, "failed to send request" );
1240         Disconnect( p_access );
1241         return VLC_EGENERIC;
1242     }
1243
1244     /* Read Answer */
1245     if( ( psz = net_Gets( p_access, p_sys->fd, pvs ) ) == NULL )
1246     {
1247         msg_Err( p_access, "failed to read answer" );
1248         goto error;
1249     }
1250     if( !strncmp( psz, "HTTP/1.", 7 ) )
1251     {
1252         p_sys->psz_protocol = "HTTP";
1253         p_sys->i_code = atoi( &psz[9] );
1254     }
1255     else if( !strncmp( psz, "ICY", 3 ) )
1256     {
1257         p_sys->psz_protocol = "ICY";
1258         p_sys->i_code = atoi( &psz[4] );
1259         p_sys->b_reconnect = true;
1260     }
1261     else
1262     {
1263         msg_Err( p_access, "invalid HTTP reply '%s'", psz );
1264         free( psz );
1265         goto error;
1266     }
1267     msg_Dbg( p_access, "protocol '%s' answer code %d",
1268              p_sys->psz_protocol, p_sys->i_code );
1269     if( !strcmp( p_sys->psz_protocol, "ICY" ) )
1270     {
1271         p_sys->b_seekable = false;
1272     }
1273     if( p_sys->i_code != 206 && p_sys->i_code != 401 )
1274     {
1275         p_sys->b_seekable = false;
1276     }
1277     /* Authentication error - We'll have to display the dialog */
1278     if( p_sys->i_code == 401 )
1279     {
1280
1281     }
1282     /* Other fatal error */
1283     else if( p_sys->i_code >= 400 )
1284     {
1285         msg_Err( p_access, "error: %s", psz );
1286         free( psz );
1287         goto error;
1288     }
1289     free( psz );
1290
1291     for( ;; )
1292     {
1293         char *psz = net_Gets( p_access, p_sys->fd, pvs );
1294         char *p;
1295         char *p_trailing;
1296
1297         if( psz == NULL )
1298         {
1299             msg_Err( p_access, "failed to read answer" );
1300             goto error;
1301         }
1302
1303         if( !vlc_object_alive (p_access) || p_sys->b_error )
1304         {
1305             free( psz );
1306             goto error;
1307         }
1308
1309         /* msg_Dbg( p_input, "Line=%s", psz ); */
1310         if( *psz == '\0' )
1311         {
1312             free( psz );
1313             break;
1314         }
1315
1316         if( ( p = strchr( psz, ':' ) ) == NULL )
1317         {
1318             msg_Err( p_access, "malformed header line: %s", psz );
1319             free( psz );
1320             goto error;
1321         }
1322         *p++ = '\0';
1323         p += strspn( p, " \t" );
1324
1325         /* trim trailing white space */
1326         p_trailing = p + strlen( p );
1327         if( p_trailing > p )
1328         {
1329             p_trailing--;
1330             while( ( *p_trailing == ' ' || *p_trailing == '\t' ) && p_trailing > p )
1331             {
1332                 *p_trailing = '\0';
1333                 p_trailing--;
1334             }
1335         }
1336
1337         if( !strcasecmp( psz, "Content-Length" ) )
1338         {
1339             uint64_t i_size = i_tell + (p_sys->i_remaining = (uint64_t)atoll( p ));
1340             if(i_size > p_access->info.i_size) {
1341                 p_sys->b_has_size = true;
1342                 p_access->info.i_size = i_size;
1343             }
1344             msg_Dbg( p_access, "this frame size=%"PRIu64, p_sys->i_remaining );
1345         }
1346         else if( !strcasecmp( psz, "Content-Range" ) ) {
1347             uint64_t i_ntell = i_tell;
1348             uint64_t i_nend = (p_access->info.i_size > 0)?(p_access->info.i_size - 1):i_tell;
1349             uint64_t i_nsize = p_access->info.i_size;
1350             sscanf(p,"bytes %"SCNu64"-%"SCNu64"/%"SCNu64,&i_ntell,&i_nend,&i_nsize);
1351             if(i_nend > i_ntell ) {
1352                 p_access->info.i_pos = i_ntell;
1353                 p_sys->i_icy_offset  = i_ntell;
1354                 p_sys->i_remaining = i_nend+1-i_ntell;
1355                 uint64_t i_size = (i_nsize > i_nend) ? i_nsize : (i_nend + 1);
1356                 if(i_size > p_access->info.i_size) {
1357                     p_sys->b_has_size = true;
1358                     p_access->info.i_size = i_size;
1359                 }
1360                 msg_Dbg( p_access, "stream size=%"PRIu64",pos=%"PRIu64",remaining=%"PRIu64,
1361                          i_nsize, i_ntell, p_sys->i_remaining);
1362             }
1363         }
1364         else if( !strcasecmp( psz, "Connection" ) ) {
1365             msg_Dbg( p_access, "Connection: %s",p );
1366             int i = -1;
1367             sscanf(p, "close%n",&i);
1368             if( i >= 0 ) {
1369                 p_sys->b_persist = false;
1370             }
1371         }
1372         else if( !strcasecmp( psz, "Location" ) )
1373         {
1374             char * psz_new_loc;
1375
1376             /* This does not follow RFC 2068, but yet if the url is not absolute,
1377              * handle it as everyone does. */
1378             if( p[0] == '/' )
1379             {
1380                 const char *psz_http_ext = p_sys->p_tls ? "s" : "" ;
1381
1382                 if( p_sys->url.i_port == ( p_sys->p_tls ? 443 : 80 ) )
1383                 {
1384                     if( asprintf(&psz_new_loc, "http%s://%s%s", psz_http_ext,
1385                                  p_sys->url.psz_host, p) < 0 )
1386                         goto error;
1387                 }
1388                 else
1389                 {
1390                     if( asprintf(&psz_new_loc, "http%s://%s:%d%s", psz_http_ext,
1391                                  p_sys->url.psz_host, p_sys->url.i_port, p) < 0 )
1392                         goto error;
1393                 }
1394             }
1395             else
1396             {
1397                 psz_new_loc = strdup( p );
1398             }
1399
1400             free( p_sys->psz_location );
1401             p_sys->psz_location = psz_new_loc;
1402         }
1403         else if( !strcasecmp( psz, "Content-Type" ) )
1404         {
1405             free( p_sys->psz_mime );
1406             p_sys->psz_mime = strdup( p );
1407             msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime );
1408         }
1409         else if( !strcasecmp( psz, "Content-Encoding" ) )
1410         {
1411             msg_Dbg( p_access, "Content-Encoding: %s", p );
1412             if( !strcasecmp( p, "identity" ) )
1413                 ;
1414 #ifdef HAVE_ZLIB_H
1415             else if( !strcasecmp( p, "gzip" ) || !strcasecmp( p, "deflate" ) )
1416                 p_sys->b_compressed = true;
1417 #endif
1418             else
1419                 msg_Warn( p_access, "Unknown content coding: %s", p );
1420         }
1421         else if( !strcasecmp( psz, "Pragma" ) )
1422         {
1423             if( !strcasecmp( psz, "Pragma: features" ) )
1424                 p_sys->b_mms = true;
1425             free( p_sys->psz_pragma );
1426             p_sys->psz_pragma = strdup( p );
1427             msg_Dbg( p_access, "Pragma: %s", p_sys->psz_pragma );
1428         }
1429         else if( !strcasecmp( psz, "Server" ) )
1430         {
1431             msg_Dbg( p_access, "Server: %s", p );
1432             if( !strncasecmp( p, "Icecast", 7 ) ||
1433                 !strncasecmp( p, "Nanocaster", 10 ) )
1434             {
1435                 /* Remember if this is Icecast
1436                  * we need to force demux in this case without breaking
1437                  *  autodetection */
1438
1439                 /* Let live 365 streams (nanocaster) piggyback on the icecast
1440                  * routine. They look very similar */
1441
1442                 p_sys->b_reconnect = true;
1443                 p_sys->b_pace_control = false;
1444                 p_sys->b_icecast = true;
1445             }
1446         }
1447         else if( !strcasecmp( psz, "Transfer-Encoding" ) )
1448         {
1449             msg_Dbg( p_access, "Transfer-Encoding: %s", p );
1450             if( !strncasecmp( p, "chunked", 7 ) )
1451             {
1452                 p_sys->b_chunked = true;
1453             }
1454         }
1455         else if( !strcasecmp( psz, "Icy-MetaInt" ) )
1456         {
1457             msg_Dbg( p_access, "Icy-MetaInt: %s", p );
1458             p_sys->i_icy_meta = atoi( p );
1459             if( p_sys->i_icy_meta < 0 )
1460                 p_sys->i_icy_meta = 0;
1461             if( p_sys->i_icy_meta > 0 )
1462                 p_sys->b_icecast = true;
1463
1464             msg_Warn( p_access, "ICY metaint=%d", p_sys->i_icy_meta );
1465         }
1466         else if( !strcasecmp( psz, "Icy-Name" ) )
1467         {
1468             free( p_sys->psz_icy_name );
1469             char *psz_tmp = strdup( p );
1470             p_sys->psz_icy_name = EnsureUTF8( psz_tmp );
1471             if( !p_sys->psz_icy_name )
1472                 free( psz_tmp );
1473             msg_Dbg( p_access, "Icy-Name: %s", p_sys->psz_icy_name );
1474
1475             p_sys->b_icecast = true; /* be on the safeside. set it here as well. */
1476             p_sys->b_reconnect = true;
1477             p_sys->b_pace_control = false;
1478         }
1479         else if( !strcasecmp( psz, "Icy-Genre" ) )
1480         {
1481             free( p_sys->psz_icy_genre );
1482             char *psz_tmp = strdup( p );
1483             p_sys->psz_icy_genre = EnsureUTF8( psz_tmp );
1484             if( !p_sys->psz_icy_genre )
1485                 free( psz_tmp );
1486             msg_Dbg( p_access, "Icy-Genre: %s", p_sys->psz_icy_genre );
1487         }
1488         else if( !strncasecmp( psz, "Icy-Notice", 10 ) )
1489         {
1490             msg_Dbg( p_access, "Icy-Notice: %s", p );
1491         }
1492         else if( !strncasecmp( psz, "icy-", 4 ) ||
1493                  !strncasecmp( psz, "ice-", 4 ) ||
1494                  !strncasecmp( psz, "x-audiocast", 11 ) )
1495         {
1496             msg_Dbg( p_access, "Meta-Info: %s: %s", psz, p );
1497         }
1498         else if( !strcasecmp( psz, "Set-Cookie" ) )
1499         {
1500             if( p_sys->cookies )
1501             {
1502                 msg_Dbg( p_access, "Accepting Cookie: %s", p );
1503                 cookie_append( p_sys->cookies, strdup(p) );
1504             }
1505             else
1506                 msg_Dbg( p_access, "We have a Cookie we won't remember: %s", p );
1507         }
1508         else if( !strcasecmp( psz, "www-authenticate" ) )
1509         {
1510             msg_Dbg( p_access, "Authentication header: %s", p );
1511             http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access),
1512                                                   &p_sys->auth, p );
1513         }
1514         else if( !strcasecmp( psz, "proxy-authenticate" ) )
1515         {
1516             msg_Dbg( p_access, "Proxy authentication header: %s", p );
1517             http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access),
1518                                                   &p_sys->proxy_auth, p );
1519         }
1520         else if( !strcasecmp( psz, "authentication-info" ) )
1521         {
1522             msg_Dbg( p_access, "Authentication Info header: %s", p );
1523             if( AuthCheckReply( p_access, p, &p_sys->url, &p_sys->auth ) )
1524                 goto error;
1525         }
1526         else if( !strcasecmp( psz, "proxy-authentication-info" ) )
1527         {
1528             msg_Dbg( p_access, "Proxy Authentication Info header: %s", p );
1529             if( AuthCheckReply( p_access, p, &p_sys->proxy, &p_sys->proxy_auth ) )
1530                 goto error;
1531         }
1532         else if( !strcasecmp( psz, "Accept-Ranges" ) )
1533         {
1534             if( !strcasecmp( p, "bytes" ) )
1535                 p_sys->b_seekable = true;
1536         }
1537
1538         free( psz );
1539     }
1540     /* We close the stream for zero length data, unless of course the
1541      * server has already promised to do this for us.
1542      */
1543     if( p_sys->b_has_size && p_sys->i_remaining == 0 && p_sys->b_persist ) {
1544         Disconnect( p_access );
1545     }
1546     return VLC_SUCCESS;
1547
1548 error:
1549     Disconnect( p_access );
1550     return VLC_EGENERIC;
1551 }
1552
1553 /*****************************************************************************
1554  * Disconnect:
1555  *****************************************************************************/
1556 static void Disconnect( access_t *p_access )
1557 {
1558     access_sys_t *p_sys = p_access->p_sys;
1559
1560     if( p_sys->p_tls != NULL)
1561     {
1562         vlc_tls_SessionDelete( p_sys->p_tls );
1563         p_sys->p_tls = NULL;
1564         p_sys->p_vs = NULL;
1565     }
1566     if( p_sys->fd != -1)
1567     {
1568         net_Close(p_sys->fd);
1569         p_sys->fd = -1;
1570     }
1571
1572 }
1573
1574 /*****************************************************************************
1575  * Cookies (FIXME: we may want to rewrite that using a nice structure to hold
1576  * them) (FIXME: only support the "domain=" param)
1577  *****************************************************************************/
1578
1579 /* Get the NAME=VALUE part of the Cookie */
1580 static char * cookie_get_content( const char * cookie )
1581 {
1582     char * ret = strdup( cookie );
1583     if( !ret ) return NULL;
1584     char * str = ret;
1585     /* Look for a ';' */
1586     while( *str && *str != ';' ) str++;
1587     /* Replace it by a end-char */
1588     if( *str == ';' ) *str = 0;
1589     return ret;
1590 }
1591
1592 /* Get the domain where the cookie is stored */
1593 static char * cookie_get_domain( const char * cookie )
1594 {
1595     const char * str = cookie;
1596     static const char domain[] = "domain=";
1597     if( !str )
1598         return NULL;
1599     /* Look for a ';' */
1600     while( *str )
1601     {
1602         if( !strncmp( str, domain, sizeof(domain) - 1 /* minus \0 */ ) )
1603         {
1604             str += sizeof(domain) - 1 /* minus \0 */;
1605             char * ret = strdup( str );
1606             /* Now remove the next ';' if present */
1607             char * ret_iter = ret;
1608             while( *ret_iter && *ret_iter != ';' ) ret_iter++;
1609             if( *ret_iter == ';' )
1610                 *ret_iter = 0;
1611             return ret;
1612         }
1613         /* Go to next ';' field */
1614         while( *str && *str != ';' ) str++;
1615         if( *str == ';' ) str++;
1616         /* skip blank */
1617         while( *str && *str == ' ' ) str++;
1618     }
1619     return NULL;
1620 }
1621
1622 /* Get NAME in the NAME=VALUE field */
1623 static char * cookie_get_name( const char * cookie )
1624 {
1625     char * ret = cookie_get_content( cookie ); /* NAME=VALUE */
1626     if( !ret ) return NULL;
1627     char * str = ret;
1628     while( *str && *str != '=' ) str++;
1629     *str = 0;
1630     return ret;
1631 }
1632
1633 /* Add a cookie in cookies, checking to see how it should be added */
1634 static void cookie_append( vlc_array_t * cookies, char * cookie )
1635 {
1636     int i;
1637
1638     if( !cookie )
1639         return;
1640
1641     char * cookie_name = cookie_get_name( cookie );
1642
1643     /* Don't send invalid cookies */
1644     if( !cookie_name )
1645         return;
1646
1647     char * cookie_domain = cookie_get_domain( cookie );
1648     for( i = 0; i < vlc_array_count( cookies ); i++ )
1649     {
1650         char * current_cookie = vlc_array_item_at_index( cookies, i );
1651         char * current_cookie_name = cookie_get_name( current_cookie );
1652         char * current_cookie_domain = cookie_get_domain( current_cookie );
1653
1654         assert( current_cookie_name );
1655
1656         bool is_domain_matching = (
1657                       ( !cookie_domain && !current_cookie_domain ) ||
1658                       ( cookie_domain && current_cookie_domain &&
1659                         !strcmp( cookie_domain, current_cookie_domain ) ) );
1660
1661         if( is_domain_matching && !strcmp( cookie_name, current_cookie_name )  )
1662         {
1663             /* Remove previous value for this cookie */
1664             free( current_cookie );
1665             vlc_array_remove( cookies, i );
1666
1667             /* Clean */
1668             free( current_cookie_name );
1669             free( current_cookie_domain );
1670             break;
1671         }
1672         free( current_cookie_name );
1673         free( current_cookie_domain );
1674     }
1675     free( cookie_name );
1676     free( cookie_domain );
1677     vlc_array_append( cookies, cookie );
1678 }
1679
1680
1681 /*****************************************************************************
1682  * HTTP authentication
1683  *****************************************************************************/
1684
1685 static void AuthReply( access_t *p_access, const char *psz_prefix,
1686                        vlc_url_t *p_url, http_auth_t *p_auth )
1687 {
1688     access_sys_t *p_sys = p_access->p_sys;
1689     char *psz_value;
1690
1691     psz_value =
1692         http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access), p_auth,
1693                                              "GET", p_url->psz_path,
1694                                              p_url->psz_username,
1695                                              p_url->psz_password );
1696     if ( psz_value == NULL )
1697         return;
1698
1699     net_Printf( p_access, p_sys->fd, p_sys->p_vs,
1700                 "%sAuthorization: %s\r\n", psz_prefix, psz_value );
1701     free( psz_value );
1702 }
1703
1704 static int AuthCheckReply( access_t *p_access, const char *psz_header,
1705                            vlc_url_t *p_url, http_auth_t *p_auth )
1706 {
1707     return
1708         http_auth_ParseAuthenticationInfoHeader( VLC_OBJECT(p_access), p_auth,
1709                                                  psz_header, "",
1710                                                  p_url->psz_path,
1711                                                  p_url->psz_username,
1712                                                  p_url->psz_password );
1713 }