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