]> git.sesse.net Git - vlc/blob - modules/access/http.c
HTTP: do not send a negative byte range
[vlc] / modules / access / http.c
1 /*****************************************************************************
2  * http.c: HTTP input module
3  *****************************************************************************
4  * Copyright (C) 2001-2008 the VideoLAN team
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
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 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 General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, 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_interface.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_input.h>
47 #include <vlc_md5.h>
48
49 #ifdef HAVE_ZLIB_H
50 #   include <zlib.h>
51 #endif
52
53 #include <assert.h>
54
55 #ifdef HAVE_PROXY_H
56 #    include "proxy.h"
57 #endif
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 CACHING_TEXT N_("Caching value in ms")
75 #define CACHING_LONGTEXT N_( \
76     "Caching value for HTTP streams. This " \
77     "value should be set in milliseconds." )
78
79 #define AGENT_TEXT N_("HTTP user agent")
80 #define AGENT_LONGTEXT N_("User agent that will be " \
81     "used for the connection.")
82
83 #define RECONNECT_TEXT N_("Auto re-connect")
84 #define RECONNECT_LONGTEXT N_( \
85     "Automatically try to reconnect to the stream in case of a sudden " \
86     "disconnect." )
87
88 #define CONTINUOUS_TEXT N_("Continuous stream")
89 #define CONTINUOUS_LONGTEXT N_("Read a file that is " \
90     "being constantly updated (for example, a JPG file on a server). " \
91     "You should not globally enable this option as it will break all other " \
92     "types of HTTP streams." )
93
94 #define FORWARD_COOKIES_TEXT N_("Forward Cookies")
95 #define FORWARD_COOKIES_LONGTEXT N_("Forward Cookies Across http redirections ")
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, NULL, PROXY_TEXT, PROXY_LONGTEXT,
105                 false );
106     add_password( "http-proxy-pwd", NULL, NULL,
107                   PROXY_PASS_TEXT, PROXY_PASS_LONGTEXT, false );
108     add_integer( "http-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
109                  CACHING_TEXT, CACHING_LONGTEXT, true );
110     add_string( "http-user-agent", COPYRIGHT_MESSAGE , NULL, AGENT_TEXT,
111                 AGENT_LONGTEXT, true );
112     add_bool( "http-reconnect", 0, NULL, RECONNECT_TEXT,
113               RECONNECT_LONGTEXT, true );
114     add_bool( "http-continuous", 0, NULL, CONTINUOUS_TEXT,
115               CONTINUOUS_LONGTEXT, true );
116     add_bool( "http-forward-cookies", 0, NULL, FORWARD_COOKIES_TEXT,
117               FORWARD_COOKIES_LONGTEXT, true );
118     add_obsolete_string("http-user");
119     add_obsolete_string("http-pwd");
120     add_shortcut( "http" );
121     add_shortcut( "https" );
122     add_shortcut( "unsv" );
123     add_shortcut( "itpc" ); /* iTunes Podcast */
124     set_callbacks( Open, Close );
125 vlc_module_end();
126
127 /*****************************************************************************
128  * Local prototypes
129  *****************************************************************************/
130
131 /* RFC 2617: Basic and Digest Access Authentication */
132 typedef struct http_auth_t
133 {
134     char *psz_realm;
135     char *psz_domain;
136     char *psz_nonce;
137     char *psz_opaque;
138     char *psz_stale;
139     char *psz_algorithm;
140     char *psz_qop;
141     int i_nonce;
142     char *psz_cnonce;
143     char *psz_HA1; /* stored H(A1) value if algorithm = "MD5-sess" */
144 } http_auth_t;
145
146 struct access_sys_t
147 {
148     int fd;
149     tls_session_t *p_tls;
150     v_socket_t    *p_vs;
151
152     /* From uri */
153     vlc_url_t url;
154     char    *psz_user_agent;
155     http_auth_t auth;
156
157     /* Proxy */
158     bool b_proxy;
159     vlc_url_t  proxy;
160     http_auth_t proxy_auth;
161     char       *psz_proxy_passbuf;
162
163     /* */
164     int        i_code;
165     const char *psz_protocol;
166     int        i_version;
167
168     char       *psz_mime;
169     char       *psz_pragma;
170     char       *psz_location;
171     bool b_mms;
172     bool b_icecast;
173     bool b_ssl;
174 #ifdef HAVE_ZLIB_H
175     bool b_compressed;
176     struct
177     {
178         z_stream   stream;
179         uint8_t   *p_buffer;
180     } inflate;
181 #endif
182
183     bool b_chunked;
184     int64_t    i_chunk;
185
186     int        i_icy_meta;
187     char       *psz_icy_name;
188     char       *psz_icy_genre;
189     char       *psz_icy_title;
190
191     int i_remaining;
192
193     bool b_seekable;
194     bool b_reconnect;
195     bool b_continuous;
196     bool b_pace_control;
197
198     vlc_array_t * cookies;
199 };
200
201 /* */
202 static int OpenWithCookies( vlc_object_t *p_this, vlc_array_t *cookies );
203
204 /* */
205 static ssize_t Read( access_t *, uint8_t *, size_t );
206 static ssize_t ReadCompressed( access_t *, uint8_t *, size_t );
207 static int Seek( access_t *, int64_t );
208 static int Control( access_t *, int, va_list );
209
210 /* */
211 static int Connect( access_t *, int64_t );
212 static int Request( access_t *p_access, int64_t i_tell );
213 static void Disconnect( access_t * );
214
215 /* Small Cookie utilities. Cookies support is partial. */
216 static char * cookie_get_content( const char * cookie );
217 static char * cookie_get_domain( const char * cookie );
218 static char * cookie_get_name( const char * cookie );
219 static void cookie_append( vlc_array_t * cookies, char * cookie );
220
221
222 static void AuthParseHeader( access_t *p_access, const char *psz_header,
223                              http_auth_t *p_auth );
224 static void AuthReply( access_t *p_acces, const char *psz_prefix,
225                        vlc_url_t *p_url, http_auth_t *p_auth );
226 static int AuthCheckReply( access_t *p_access, const char *psz_header,
227                            vlc_url_t *p_url, http_auth_t *p_auth );
228 static void AuthReset( http_auth_t *p_auth );
229
230 /*****************************************************************************
231  * Open:
232  *****************************************************************************/
233 static int Open( vlc_object_t *p_this )
234 {
235     return OpenWithCookies( p_this, NULL );
236 }
237
238 static int OpenWithCookies( vlc_object_t *p_this, vlc_array_t *cookies )
239 {
240     access_t     *p_access = (access_t*)p_this;
241     access_sys_t *p_sys;
242     char         *psz, *p;
243     /* Only forward an store cookies if the corresponding option is activated */
244     bool   b_forward_cookies = var_CreateGetBool( p_access, "http-forward-cookies" );
245     vlc_array_t * saved_cookies = b_forward_cookies ? (cookies ?: vlc_array_new()) : NULL;
246
247     /* Set up p_access */
248     STANDARD_READ_ACCESS_INIT;
249 #ifdef HAVE_ZLIB_H
250     p_access->pf_read = ReadCompressed;
251 #endif
252     p_sys->fd = -1;
253     p_sys->b_proxy = false;
254     p_sys->psz_proxy_passbuf = NULL;
255     p_sys->i_version = 1;
256     p_sys->b_seekable = true;
257     p_sys->psz_mime = NULL;
258     p_sys->psz_pragma = NULL;
259     p_sys->b_mms = false;
260     p_sys->b_icecast = false;
261     p_sys->psz_location = NULL;
262     p_sys->psz_user_agent = NULL;
263     p_sys->b_pace_control = true;
264     p_sys->b_ssl = false;
265 #ifdef HAVE_ZLIB_H
266     p_sys->b_compressed = false;
267     /* 15 is the max windowBits, +32 to enable optional gzip decoding */
268     if( inflateInit2( &p_sys->inflate.stream, 32+15 ) != Z_OK )
269         msg_Warn( p_access, "Error during zlib initialisation: %s",
270                   p_sys->inflate.stream.msg );
271     if( zlibCompileFlags() & (1<<17) )
272         msg_Warn( p_access, "Your zlib was compiled without gzip support." );
273     p_sys->inflate.p_buffer = NULL;
274 #endif
275     p_sys->p_tls = NULL;
276     p_sys->p_vs = NULL;
277     p_sys->i_icy_meta = 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
283     p_sys->cookies = saved_cookies;
284
285     /* Parse URI - remove spaces */
286     p = psz = strdup( p_access->psz_path );
287     while( (p = strchr( p, ' ' )) != NULL )
288         *p = '+';
289     vlc_UrlParse( &p_sys->url, psz, 0 );
290     free( psz );
291
292     if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
293     {
294         msg_Warn( p_access, "invalid host" );
295         goto error;
296     }
297     if( !strncmp( p_access->psz_access, "https", 5 ) )
298     {
299         /* HTTP over SSL */
300         p_sys->b_ssl = true;
301         if( p_sys->url.i_port <= 0 )
302             p_sys->url.i_port = 443;
303     }
304     else
305     {
306         if( p_sys->url.i_port <= 0 )
307             p_sys->url.i_port = 80;
308     }
309
310     /* Do user agent */
311     p_sys->psz_user_agent = var_CreateGetString( p_access, "http-user-agent" );
312
313     /* Check proxy */
314     psz = var_CreateGetNonEmptyString( p_access, "http-proxy" );
315     if( psz )
316     {
317         p_sys->b_proxy = true;
318         vlc_UrlParse( &p_sys->proxy, psz, 0 );
319         free( psz );
320     }
321 #ifdef HAVE_PROXY_H
322     else
323     {
324         pxProxyFactory *pf = px_proxy_factory_new();
325         if (pf)
326         {
327             char *buf;
328             int i;
329             i=asprintf(&buf, "%s://%s", p_access->psz_access, p_access->psz_path);
330             if (i >= 0)
331             {
332                 msg_Dbg(p_access, "asking libproxy about url '%s'", buf);
333                 char **proxies = px_proxy_factory_get_proxies(pf, buf);
334                 if (proxies[0])
335                 {
336                     msg_Dbg(p_access, "libproxy suggest to use '%s'", proxies[0]);
337                     if(strcmp(proxies[0],"direct://") != 0) 
338                     {
339                         p_sys->b_proxy = true;
340                         vlc_UrlParse( &p_sys->proxy, proxies[0], 0);
341                     }
342                 }
343                 for(i=0;proxies[i];i++) free(proxies[i]);
344                 free(proxies);
345                 free(buf);
346             }
347             px_proxy_factory_free(pf);
348         }
349         else
350         {
351             msg_Err(p_access, "Allocating memory for libproxy failed");
352         }
353     }
354 #elif HAVE_GETENV
355     else
356     {
357         psz = getenv( "http_proxy" );
358         if( psz )
359         {
360             p_sys->b_proxy = true;
361             vlc_UrlParse( &p_sys->proxy, psz, 0 );
362         }
363     }
364 #endif
365     if( psz ) /* No, this is NOT a use-after-free error */
366     {
367         psz = var_CreateGetNonEmptyString( p_access, "http-proxy-pwd" );
368         if( psz )
369             p_sys->proxy.psz_password = p_sys->psz_proxy_passbuf = psz;
370     }
371
372     if( p_sys->b_proxy )
373     {
374         if( p_sys->proxy.psz_host == NULL || *p_sys->proxy.psz_host == '\0' )
375         {
376             msg_Warn( p_access, "invalid proxy host" );
377             goto error;
378         }
379         if( p_sys->proxy.i_port <= 0 )
380         {
381             p_sys->proxy.i_port = 80;
382         }
383     }
384
385     msg_Dbg( p_access, "http: server='%s' port=%d file='%s",
386              p_sys->url.psz_host, p_sys->url.i_port, p_sys->url.psz_path );
387     if( p_sys->b_proxy )
388     {
389         msg_Dbg( p_access, "      proxy %s:%d", p_sys->proxy.psz_host,
390                  p_sys->proxy.i_port );
391     }
392     if( p_sys->url.psz_username && *p_sys->url.psz_username )
393     {
394         msg_Dbg( p_access, "      user='%s'", p_sys->url.psz_username );
395     }
396
397     p_sys->b_reconnect = var_CreateGetBool( p_access, "http-reconnect" );
398     p_sys->b_continuous = var_CreateGetBool( p_access, "http-continuous" );
399
400 connect:
401     /* Connect */
402     switch( Connect( p_access, 0 ) )
403     {
404         case -1:
405             goto error;
406
407         case -2:
408             /* Retry with http 1.0 */
409             msg_Dbg( p_access, "switching to HTTP version 1.0" );
410             p_sys->i_version = 0;
411             p_sys->b_seekable = false;
412
413             if( p_access->b_die || Connect( p_access, 0 ) )
414                 goto error;
415
416 #ifndef NDEBUG
417         case 0:
418             break;
419
420         default:
421             msg_Err( p_access, "You should not be here" );
422             abort();
423 #endif
424     }
425
426     if( p_sys->i_code == 401 )
427     {
428         char *psz_login = NULL, *psz_password = NULL;
429         char psz_msg[250];
430         int i_ret;
431         /* FIXME ? */
432         if( p_sys->url.psz_username && p_sys->url.psz_password &&
433             p_sys->auth.psz_nonce && p_sys->auth.i_nonce == 0 )
434         {
435             goto connect;
436         }
437         snprintf( psz_msg, 250,
438             _("Please enter a valid login name and a password for realm %s."),
439             p_sys->auth.psz_realm );
440         msg_Dbg( p_access, "authentication failed for realm %s",
441             p_sys->auth.psz_realm );
442         i_ret = intf_UserLoginPassword( p_access, _("HTTP authentication"),
443                                         psz_msg, &psz_login, &psz_password );
444         if( i_ret == DIALOG_OK_YES )
445         {
446             msg_Dbg( p_access, "retrying with user=%s, pwd=%s",
447                         psz_login, psz_password );
448             if( psz_login ) p_sys->url.psz_username = strdup( psz_login );
449             if( psz_password ) p_sys->url.psz_password = strdup( psz_password );
450             free( psz_login );
451             free( psz_password );
452             goto connect;
453         }
454         else
455         {
456             free( psz_login );
457             free( psz_password );
458             goto error;
459         }
460     }
461
462     if( ( p_sys->i_code == 301 || p_sys->i_code == 302 ||
463           p_sys->i_code == 303 || p_sys->i_code == 307 ) &&
464         p_sys->psz_location && *p_sys->psz_location )
465     {
466         msg_Dbg( p_access, "redirection to %s", p_sys->psz_location );
467
468         /* Do not accept redirection outside of HTTP works */
469         if( strncmp( p_sys->psz_location, "http", 4 )
470          || ( ( p_sys->psz_location[4] != ':' ) /* HTTP */
471            && strncmp( p_sys->psz_location + 4, "s:", 2 ) /* HTTP/SSL */ ) )
472         {
473             msg_Err( p_access, "insecure redirection ignored" );
474             goto error;
475         }
476         free( p_access->psz_path );
477         p_access->psz_path = strdup( p_sys->psz_location );
478         /* Clean up current Open() run */
479         vlc_UrlClean( &p_sys->url );
480         AuthReset( &p_sys->auth );
481         vlc_UrlClean( &p_sys->proxy );
482         free( p_sys->psz_proxy_passbuf );
483         AuthReset( &p_sys->proxy_auth );
484         free( p_sys->psz_mime );
485         free( p_sys->psz_pragma );
486         free( p_sys->psz_location );
487         free( p_sys->psz_user_agent );
488
489         Disconnect( p_access );
490         cookies = p_sys->cookies;
491         free( p_sys );
492
493         /* Do new Open() run with new data */
494         return OpenWithCookies( p_this, cookies );
495     }
496
497     if( p_sys->b_mms )
498     {
499         msg_Dbg( p_access, "this is actually a live mms server, BAIL" );
500         goto error;
501     }
502
503     if( !strcmp( p_sys->psz_protocol, "ICY" ) || p_sys->b_icecast )
504     {
505         if( p_sys->psz_mime && strcasecmp( p_sys->psz_mime, "application/ogg" ) )
506         {
507             if( !strcasecmp( p_sys->psz_mime, "video/nsv" ) ||
508                 !strcasecmp( p_sys->psz_mime, "video/nsa" ) )
509             {
510                 free( p_access->psz_demux );
511                 p_access->psz_demux = strdup( "nsv" );
512             }
513             else if( !strcasecmp( p_sys->psz_mime, "audio/aac" ) ||
514                      !strcasecmp( p_sys->psz_mime, "audio/aacp" ) )
515             {
516                 free( p_access->psz_demux );
517                 p_access->psz_demux = strdup( "m4a" );
518             }
519             else if( !strcasecmp( p_sys->psz_mime, "audio/mpeg" ) )
520             {
521                 free( p_access->psz_demux );
522                 p_access->psz_demux = strdup( "mp3" );
523             }
524
525             msg_Info( p_access, "Raw-audio server found, %s demuxer selected",
526                       p_access->psz_demux );
527
528 #if 0       /* Doesn't work really well because of the pre-buffering in
529              * shoutcast servers (the buffer content will be sent as fast as
530              * possible). */
531             p_sys->b_pace_control = false;
532 #endif
533         }
534         else if( !p_sys->psz_mime )
535         {
536             free( p_access->psz_demux );
537             /* Shoutcast */
538             p_access->psz_demux = strdup( "mp3" );
539         }
540         /* else probably Ogg Vorbis */
541     }
542     else if( !strcasecmp( p_access->psz_access, "unsv" ) &&
543              p_sys->psz_mime &&
544              !strcasecmp( p_sys->psz_mime, "misc/ultravox" ) )
545     {
546         free( p_access->psz_demux );
547         /* Grrrr! detect ultravox server and force NSV demuxer */
548         p_access->psz_demux = strdup( "nsv" );
549     }
550     else if( !strcmp( p_access->psz_access, "itpc" ) )
551     {
552         free( p_access->psz_demux );
553         p_access->psz_demux = strdup( "podcast" );
554     }
555     else if( p_sys->psz_mime &&
556              !strncasecmp( p_sys->psz_mime, "application/xspf+xml", 20 ) &&
557              ( memchr( " ;\t", p_sys->psz_mime[20], 4 ) != NULL ) )
558     {
559         free( p_access->psz_demux );
560         p_access->psz_demux = strdup( "xspf-open" );
561     }
562
563     if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );
564
565     /* PTS delay */
566     var_Create( p_access, "http-caching", VLC_VAR_INTEGER |VLC_VAR_DOINHERIT );
567
568     return VLC_SUCCESS;
569
570 error:
571     vlc_UrlClean( &p_sys->url );
572     vlc_UrlClean( &p_sys->proxy );
573     free( p_sys->psz_proxy_passbuf );
574     free( p_sys->psz_mime );
575     free( p_sys->psz_pragma );
576     free( p_sys->psz_location );
577     free( p_sys->psz_user_agent );
578
579     Disconnect( p_access );
580     free( p_sys );
581     return VLC_EGENERIC;
582 }
583
584 /*****************************************************************************
585  * Close:
586  *****************************************************************************/
587 static void Close( vlc_object_t *p_this )
588 {
589     access_t     *p_access = (access_t*)p_this;
590     access_sys_t *p_sys = p_access->p_sys;
591
592     vlc_UrlClean( &p_sys->url );
593     AuthReset( &p_sys->auth );
594     vlc_UrlClean( &p_sys->proxy );
595     AuthReset( &p_sys->proxy_auth );
596
597     free( p_sys->psz_mime );
598     free( p_sys->psz_pragma );
599     free( p_sys->psz_location );
600
601     free( p_sys->psz_icy_name );
602     free( p_sys->psz_icy_genre );
603     free( p_sys->psz_icy_title );
604
605     free( p_sys->psz_user_agent );
606
607     Disconnect( p_access );
608
609     if( p_sys->cookies )
610     {
611         int i;
612         for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
613             free(vlc_array_item_at_index( p_sys->cookies, i ));
614         vlc_array_destroy( p_sys->cookies );
615     }
616
617 #ifdef HAVE_ZLIB_H
618     inflateEnd( &p_sys->inflate.stream );
619     free( p_sys->inflate.p_buffer );
620 #endif
621
622     free( p_sys );
623 }
624
625 /*****************************************************************************
626  * Read: Read up to i_len bytes from the http connection and place in
627  * p_buffer. Return the actual number of bytes read
628  *****************************************************************************/
629 static int ReadICYMeta( access_t *p_access );
630 static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
631 {
632     access_sys_t *p_sys = p_access->p_sys;
633     int i_read;
634
635     if( p_sys->fd < 0 )
636     {
637         p_access->info.b_eof = true;
638         return 0;
639     }
640
641     if( p_access->info.i_size > 0 &&
642         i_len + p_access->info.i_pos > p_access->info.i_size )
643     {
644         if( ( i_len = p_access->info.i_size - p_access->info.i_pos ) == 0 )
645         {
646             p_access->info.b_eof = true;
647             return 0;
648         }
649     }
650
651     if( p_sys->b_chunked )
652     {
653         if( p_sys->i_chunk < 0 )
654         {
655             p_access->info.b_eof = true;
656             return 0;
657         }
658
659         if( p_sys->i_chunk <= 0 )
660         {
661             char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, p_sys->p_vs );
662             /* read the chunk header */
663             if( psz == NULL )
664             {
665                 /* fatal error - end of file */
666                 msg_Dbg( p_access, "failed reading chunk-header line" );
667                 return 0;
668             }
669             p_sys->i_chunk = strtoll( psz, NULL, 16 );
670             free( psz );
671
672             if( p_sys->i_chunk <= 0 )   /* eof */
673             {
674                 p_sys->i_chunk = -1;
675                 p_access->info.b_eof = true;
676                 return 0;
677             }
678         }
679
680         if( i_len > p_sys->i_chunk )
681         {
682             i_len = p_sys->i_chunk;
683         }
684     }
685
686     if( p_sys->b_continuous && (ssize_t)i_len > p_sys->i_remaining )
687     {
688         /* Only ask for the remaining length */
689         int i_new_len = p_sys->i_remaining;
690         if( i_new_len == 0 )
691         {
692             Request( p_access, 0 );
693             i_read = Read( p_access, p_buffer, i_len );
694             return i_read;
695         }
696         i_len = i_new_len;
697     }
698
699     if( p_sys->i_icy_meta > 0 && p_access->info.i_pos > 0 )
700     {
701         int64_t i_next = p_sys->i_icy_meta -
702                                     p_access->info.i_pos % p_sys->i_icy_meta;
703
704         if( i_next == p_sys->i_icy_meta )
705         {
706             if( ReadICYMeta( p_access ) )
707             {
708                 p_access->info.b_eof = true;
709                 return -1;
710             }
711         }
712         if( i_len > i_next )
713             i_len = i_next;
714     }
715
716     i_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, p_buffer, i_len, false );
717
718     if( i_read > 0 )
719     {
720         p_access->info.i_pos += i_read;
721
722         if( p_sys->b_chunked )
723         {
724             p_sys->i_chunk -= i_read;
725             if( p_sys->i_chunk <= 0 )
726             {
727                 /* read the empty line */
728                 char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, p_sys->p_vs );
729                 free( psz );
730             }
731         }
732     }
733     else if( i_read == 0 )
734     {
735         /*
736          * I very much doubt that this will work.
737          * If i_read == 0, the connection *IS* dead, so the only
738          * sensible thing to do is Disconnect() and then retry.
739          * Otherwise, I got recv() completely wrong. -- Courmisch
740          */
741         if( p_sys->b_continuous )
742         {
743             Request( p_access, 0 );
744             p_sys->b_continuous = false;
745             i_read = Read( p_access, p_buffer, i_len );
746             p_sys->b_continuous = true;
747         }
748         Disconnect( p_access );
749         if( p_sys->b_reconnect )
750         {
751             msg_Dbg( p_access, "got disconnected, trying to reconnect" );
752             if( Connect( p_access, p_access->info.i_pos ) )
753             {
754                 msg_Dbg( p_access, "reconnection failed" );
755             }
756             else
757             {
758                 p_sys->b_reconnect = false;
759                 i_read = Read( p_access, p_buffer, i_len );
760                 p_sys->b_reconnect = true;
761             }
762         }
763
764         if( i_read == 0 ) p_access->info.b_eof = true;
765     }
766
767     if( p_sys->b_continuous )
768     {
769         p_sys->i_remaining -= i_read;
770     }
771
772     return i_read;
773 }
774
775 static int ReadICYMeta( access_t *p_access )
776 {
777     access_sys_t *p_sys = p_access->p_sys;
778
779     uint8_t buffer;
780     char *p, *psz_meta;
781     int i_read;
782
783     /* Read meta data length */
784     i_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, &buffer, 1,
785                        true );
786     if( i_read <= 0 )
787         return VLC_EGENERIC;
788     if( buffer == 0 )
789         return VLC_SUCCESS;
790
791     i_read = buffer << 4;
792     /* msg_Dbg( p_access, "ICY meta size=%u", i_read); */
793
794     psz_meta = malloc( i_read + 1 );
795     if( net_Read( p_access, p_sys->fd, p_sys->p_vs,
796                   (uint8_t *)psz_meta, i_read, true ) != i_read )
797         return VLC_EGENERIC;
798
799     psz_meta[i_read] = '\0'; /* Just in case */
800
801     /* msg_Dbg( p_access, "icy-meta=%s", psz_meta ); */
802
803     /* Now parse the meta */
804     /* Look for StreamTitle= */
805     p = strcasestr( (char *)psz_meta, "StreamTitle=" );
806     if( p )
807     {
808         p += strlen( "StreamTitle=" );
809         if( *p == '\'' || *p == '"' )
810         {
811             char closing[] = { p[0], ';', '\0' };
812             char *psz = strstr( &p[1], closing );
813             if( !psz )
814                 psz = strchr( &p[1], ';' );
815
816             if( psz ) *psz = '\0';
817         }
818         else
819         {
820             char *psz = strchr( &p[1], ';' );
821             if( psz ) *psz = '\0';
822         }
823
824         if( !p_sys->psz_icy_title ||
825             strcmp( p_sys->psz_icy_title, &p[1] ) )
826         {
827             free( p_sys->psz_icy_title );
828             p_sys->psz_icy_title = strdup( &p[1] );
829             p_access->info.i_update |= INPUT_UPDATE_META;
830
831             msg_Dbg( p_access, "New Title=%s", p_sys->psz_icy_title );
832         }
833     }
834     free( psz_meta );
835
836     return VLC_SUCCESS;
837 }
838
839 #ifdef HAVE_ZLIB_H
840 static ssize_t ReadCompressed( access_t *p_access, uint8_t *p_buffer,
841                                size_t i_len )
842 {
843     access_sys_t *p_sys = p_access->p_sys;
844
845     if( p_sys->b_compressed )
846     {
847         int i_ret;
848
849         if( !p_sys->inflate.p_buffer )
850             p_sys->inflate.p_buffer = malloc( 256 * 1024 );
851
852         if( p_sys->inflate.stream.avail_in == 0 )
853         {
854             ssize_t i_read = Read( p_access, p_sys->inflate.p_buffer + p_sys->inflate.stream.avail_in, 256 * 1024 );
855             if( i_read <= 0 ) return i_read;
856             p_sys->inflate.stream.next_in = p_sys->inflate.p_buffer;
857             p_sys->inflate.stream.avail_in = i_read;
858         }
859
860         p_sys->inflate.stream.avail_out = i_len;
861         p_sys->inflate.stream.next_out = p_buffer;
862
863         i_ret = inflate( &p_sys->inflate.stream, Z_SYNC_FLUSH );
864         msg_Warn( p_access, "inflate return value: %d, %s", i_ret, p_sys->inflate.stream.msg );
865
866         return i_len - p_sys->inflate.stream.avail_out;
867     }
868     else
869     {
870         return Read( p_access, p_buffer, i_len );
871     }
872 }
873 #endif
874
875 /*****************************************************************************
876  * Seek: close and re-open a connection at the right place
877  *****************************************************************************/
878 static int Seek( access_t *p_access, int64_t i_pos )
879 {
880     msg_Dbg( p_access, "trying to seek to %"PRId64, i_pos );
881
882     Disconnect( p_access );
883
884     if( Connect( p_access, i_pos ) )
885     {
886         msg_Err( p_access, "seek failed" );
887         p_access->info.b_eof = true;
888         return VLC_EGENERIC;
889     }
890     return VLC_SUCCESS;
891 }
892
893 /*****************************************************************************
894  * Control:
895  *****************************************************************************/
896 static int Control( access_t *p_access, int i_query, va_list args )
897 {
898     access_sys_t *p_sys = p_access->p_sys;
899     bool   *pb_bool;
900     int          *pi_int;
901     int64_t      *pi_64;
902     vlc_meta_t   *p_meta;
903
904     switch( i_query )
905     {
906         /* */
907         case ACCESS_CAN_SEEK:
908             pb_bool = (bool*)va_arg( args, bool* );
909             *pb_bool = p_sys->b_seekable;
910             break;
911         case ACCESS_CAN_FASTSEEK:
912             pb_bool = (bool*)va_arg( args, bool* );
913             *pb_bool = false;
914             break;
915         case ACCESS_CAN_PAUSE:
916         case ACCESS_CAN_CONTROL_PACE:
917             pb_bool = (bool*)va_arg( args, bool* );
918
919 #if 0       /* Disable for now until we have a clock synchro algo
920              * which works with something else than MPEG over UDP */
921             *pb_bool = p_sys->b_pace_control;
922 #endif
923             *pb_bool = true;
924             break;
925
926         /* */
927         case ACCESS_GET_MTU:
928             pi_int = (int*)va_arg( args, int * );
929             *pi_int = 0;
930             break;
931
932         case ACCESS_GET_PTS_DELAY:
933             pi_64 = (int64_t*)va_arg( args, int64_t * );
934             *pi_64 = (int64_t)var_GetInteger( p_access, "http-caching" ) * 1000;
935             break;
936
937         /* */
938         case ACCESS_SET_PAUSE_STATE:
939             break;
940
941         case ACCESS_GET_META:
942             p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t* );
943
944             if( p_sys->psz_icy_name )
945                 vlc_meta_Set( p_meta, vlc_meta_Title, p_sys->psz_icy_name );
946             if( p_sys->psz_icy_genre )
947                 vlc_meta_Set( p_meta, vlc_meta_Genre, p_sys->psz_icy_genre );
948             if( p_sys->psz_icy_title )
949                 vlc_meta_Set( p_meta, vlc_meta_NowPlaying, p_sys->psz_icy_title );
950             break;
951
952         case ACCESS_GET_CONTENT_TYPE:
953             *va_arg( args, char ** ) =
954                 p_sys->psz_mime ? strdup( p_sys->psz_mime ) : NULL;
955             break;
956
957         case ACCESS_GET_TITLE_INFO:
958         case ACCESS_SET_TITLE:
959         case ACCESS_SET_SEEKPOINT:
960         case ACCESS_SET_PRIVATE_ID_STATE:
961             return VLC_EGENERIC;
962
963         default:
964             msg_Warn( p_access, "unimplemented query in control" );
965             return VLC_EGENERIC;
966
967     }
968     return VLC_SUCCESS;
969 }
970
971 /*****************************************************************************
972  * Connect:
973  *****************************************************************************/
974 static int Connect( access_t *p_access, int64_t i_tell )
975 {
976     access_sys_t   *p_sys = p_access->p_sys;
977     vlc_url_t      srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url;
978
979     /* Clean info */
980     free( p_sys->psz_location );
981     free( p_sys->psz_mime );
982     free( p_sys->psz_pragma );
983
984     free( p_sys->psz_icy_genre );
985     free( p_sys->psz_icy_name );
986     free( p_sys->psz_icy_title );
987
988
989     p_sys->psz_location = NULL;
990     p_sys->psz_mime = NULL;
991     p_sys->psz_pragma = NULL;
992     p_sys->b_mms = false;
993     p_sys->b_chunked = false;
994     p_sys->i_chunk = 0;
995     p_sys->i_icy_meta = 0;
996     p_sys->psz_icy_name = NULL;
997     p_sys->psz_icy_genre = NULL;
998     p_sys->psz_icy_title = NULL;
999
1000     p_access->info.i_size = 0;
1001     p_access->info.i_pos  = i_tell;
1002     p_access->info.b_eof  = false;
1003
1004
1005     /* Open connection */
1006     p_sys->fd = net_ConnectTCP( p_access, srv.psz_host, srv.i_port );
1007     if( p_sys->fd == -1 )
1008     {
1009         msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port );
1010         return -1;
1011     }
1012
1013     /* Initialize TLS/SSL session */
1014     if( p_sys->b_ssl == true )
1015     {
1016         /* CONNECT to establish TLS tunnel through HTTP proxy */
1017         if( p_sys->b_proxy )
1018         {
1019             char *psz;
1020             unsigned i_status = 0;
1021
1022             if( p_sys->i_version == 0 )
1023             {
1024                 /* CONNECT is not in HTTP/1.0 */
1025                 Disconnect( p_access );
1026                 return -1;
1027             }
1028
1029             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
1030                         "CONNECT %s:%d HTTP/1.%d\r\nHost: %s:%d\r\n\r\n",
1031                         p_sys->url.psz_host, p_sys->url.i_port,
1032                         p_sys->i_version,
1033                         p_sys->url.psz_host, p_sys->url.i_port);
1034
1035             psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
1036             if( psz == NULL )
1037             {
1038                 msg_Err( p_access, "cannot establish HTTP/TLS tunnel" );
1039                 Disconnect( p_access );
1040                 return -1;
1041             }
1042
1043             sscanf( psz, "HTTP/%*u.%*u %3u", &i_status );
1044             free( psz );
1045
1046             if( ( i_status / 100 ) != 2 )
1047             {
1048                 msg_Err( p_access, "HTTP/TLS tunnel through proxy denied" );
1049                 Disconnect( p_access );
1050                 return -1;
1051             }
1052
1053             do
1054             {
1055                 psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
1056                 if( psz == NULL )
1057                 {
1058                     msg_Err( p_access, "HTTP proxy connection failed" );
1059                     Disconnect( p_access );
1060                     return -1;
1061                 }
1062
1063                 if( *psz == '\0' )
1064                     i_status = 0;
1065
1066                 free( psz );
1067
1068                 if( p_access->b_die || p_access->b_error )
1069                 {
1070                     Disconnect( p_access );
1071                     return -1;
1072                 }
1073             }
1074             while( i_status );
1075         }
1076
1077         /* TLS/SSL handshake */
1078         p_sys->p_tls = tls_ClientCreate( VLC_OBJECT(p_access), p_sys->fd,
1079                                          srv.psz_host );
1080         if( p_sys->p_tls == NULL )
1081         {
1082             msg_Err( p_access, "cannot establish HTTP/TLS session" );
1083             Disconnect( p_access );
1084             return -1;
1085         }
1086         p_sys->p_vs = &p_sys->p_tls->sock;
1087     }
1088
1089     return Request( p_access, i_tell ) ? -2 : 0;
1090 }
1091
1092
1093 static int Request( access_t *p_access, int64_t i_tell )
1094 {
1095     access_sys_t   *p_sys = p_access->p_sys;
1096     char           *psz ;
1097     v_socket_t     *pvs = p_sys->p_vs;
1098
1099     if( p_sys->b_proxy )
1100     {
1101         if( p_sys->url.psz_path )
1102         {
1103             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
1104                         "GET http://%s:%d%s HTTP/1.%d\r\n",
1105                         p_sys->url.psz_host, p_sys->url.i_port,
1106                         p_sys->url.psz_path, p_sys->i_version );
1107         }
1108         else
1109         {
1110             net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
1111                         "GET http://%s:%d/ HTTP/1.%d\r\n",
1112                         p_sys->url.psz_host, p_sys->url.i_port,
1113                         p_sys->i_version );
1114         }
1115     }
1116     else
1117     {
1118         const char *psz_path = p_sys->url.psz_path;
1119         if( !psz_path || !*psz_path )
1120         {
1121             psz_path = "/";
1122         }
1123         if( p_sys->url.i_port != (pvs ? 443 : 80) )
1124         {
1125             net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1126                         "GET %s HTTP/1.%d\r\nHost: %s:%d\r\n",
1127                         psz_path, p_sys->i_version, p_sys->url.psz_host,
1128                         p_sys->url.i_port );
1129         }
1130         else
1131         {
1132             net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1133                         "GET %s HTTP/1.%d\r\nHost: %s\r\n",
1134                         psz_path, p_sys->i_version, p_sys->url.psz_host );
1135         }
1136     }
1137     /* User Agent */
1138     net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "User-Agent: %s\r\n",
1139                 p_sys->psz_user_agent );
1140     /* Offset */
1141     if( p_sys->i_version == 1 )
1142     {
1143         net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1144                     "Range: bytes=%"PRIu64"-\r\n", i_tell );
1145     }
1146
1147     /* Cookies */
1148     if( p_sys->cookies )
1149     {
1150         int i;
1151         for( i = 0; i < vlc_array_count( p_sys->cookies ); i++ )
1152         {
1153             const char * cookie = vlc_array_item_at_index( p_sys->cookies, i );
1154             char * psz_cookie_content = cookie_get_content( cookie );
1155             char * psz_cookie_domain = cookie_get_domain( cookie );
1156
1157             assert( psz_cookie_content );
1158
1159             /* FIXME: This is clearly not conforming to the rfc */
1160             bool is_in_right_domain = (!psz_cookie_domain || strstr( p_sys->url.psz_host, psz_cookie_domain ));
1161
1162             if( is_in_right_domain )
1163             {
1164                 msg_Dbg( p_access, "Sending Cookie %s", psz_cookie_content );
1165                 if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "Cookie: %s\r\n", psz_cookie_content ) < 0 )
1166                     msg_Err( p_access, "failed to send Cookie" );
1167             }
1168             free( psz_cookie_content );
1169             free( psz_cookie_domain );
1170         }
1171     }
1172
1173     /* Authentication */
1174     if( p_sys->url.psz_username || p_sys->url.psz_password )
1175         AuthReply( p_access, "", &p_sys->url, &p_sys->auth );
1176
1177     /* Proxy Authentication */
1178     if( p_sys->proxy.psz_username || p_sys->proxy.psz_password )
1179         AuthReply( p_access, "Proxy-", &p_sys->proxy, &p_sys->proxy_auth );
1180
1181     /* ICY meta data request */
1182     net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "Icy-MetaData: 1\r\n" );
1183
1184
1185     if( p_sys->b_continuous )
1186     {
1187         net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs,
1188                     "Connection: Keep-Alive\r\n" );
1189     }
1190     else if( p_sys->i_version == 1 )
1191     {
1192         net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs,
1193                     "Connection: Close\r\n");
1194     }
1195
1196     if( net_Printf( VLC_OBJECT(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( VLC_OBJECT(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( VLC_OBJECT(p_access), p_sys->fd, pvs );
1253         char *p;
1254
1255         if( psz == NULL )
1256         {
1257             msg_Err( p_access, "failed to read answer" );
1258             goto error;
1259         }
1260
1261         if( p_access->b_die || p_access->b_error )
1262         {
1263             free( psz );
1264             goto error;
1265         }
1266
1267         /* msg_Dbg( p_input, "Line=%s", psz ); */
1268         if( *psz == '\0' )
1269         {
1270             free( psz );
1271             break;
1272         }
1273
1274         if( ( p = strchr( psz, ':' ) ) == NULL )
1275         {
1276             msg_Err( p_access, "malformed header line: %s", psz );
1277             free( psz );
1278             goto error;
1279         }
1280         *p++ = '\0';
1281         while( *p == ' ' ) p++;
1282
1283         if( !strcasecmp( psz, "Content-Length" ) )
1284         {
1285             if( p_sys->b_continuous )
1286             {
1287                 p_access->info.i_size = -1;
1288                 msg_Dbg( p_access, "this frame size=%lld", atoll(p ) );
1289                 p_sys->i_remaining = atoll( p );
1290             }
1291             else
1292             {
1293                 p_access->info.i_size = i_tell + atoll( p );
1294                 msg_Dbg( p_access, "stream size=%"PRId64, p_access->info.i_size );
1295             }
1296         }
1297         else if( !strcasecmp( psz, "Location" ) )
1298         {
1299             char * psz_new_loc;
1300
1301             /* This does not follow RFC 2068, but yet if the url is not absolute,
1302              * handle it as everyone does. */
1303             if( p[0] == '/' )
1304             {
1305                 const char *psz_http_ext = p_sys->b_ssl ? "s" : "" ;
1306
1307                 if( p_sys->url.i_port == ( p_sys->b_ssl ? 443 : 80 ) )
1308                 {
1309                     if( asprintf(&psz_new_loc, "http%s://%s%s", psz_http_ext,
1310                                  p_sys->url.psz_host, p) < 0 )
1311                         goto error;
1312                 }
1313                 else
1314                 {
1315                     if( asprintf(&psz_new_loc, "http%s://%s:%d%s", psz_http_ext,
1316                                  p_sys->url.psz_host, p_sys->url.i_port, p) < 0 )
1317                         goto error;
1318                 }
1319             }
1320             else
1321             {
1322                 psz_new_loc = strdup( p );
1323             }
1324
1325             free( p_sys->psz_location );
1326             p_sys->psz_location = psz_new_loc;
1327         }
1328         else if( !strcasecmp( psz, "Content-Type" ) )
1329         {
1330             free( p_sys->psz_mime );
1331             p_sys->psz_mime = strdup( p );
1332             msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime );
1333         }
1334         else if( !strcasecmp( psz, "Content-Encoding" ) )
1335         {
1336             msg_Dbg( p_access, "Content-Encoding: %s", p );
1337             if( strcasecmp( p, "identity" ) )
1338 #ifdef HAVE_ZLIB_H
1339                 p_sys->b_compressed = true;
1340 #else
1341                 msg_Warn( p_access, "Compressed content not supported. Rebuild with zlib support." );
1342 #endif
1343         }
1344         else if( !strcasecmp( psz, "Pragma" ) )
1345         {
1346             if( !strcasecmp( psz, "Pragma: features" ) )
1347                 p_sys->b_mms = true;
1348             free( p_sys->psz_pragma );
1349             p_sys->psz_pragma = strdup( p );
1350             msg_Dbg( p_access, "Pragma: %s", p_sys->psz_pragma );
1351         }
1352         else if( !strcasecmp( psz, "Server" ) )
1353         {
1354             msg_Dbg( p_access, "Server: %s", p );
1355             if( !strncasecmp( p, "Icecast", 7 ) ||
1356                 !strncasecmp( p, "Nanocaster", 10 ) )
1357             {
1358                 /* Remember if this is Icecast
1359                  * we need to force demux in this case without breaking
1360                  *  autodetection */
1361
1362                 /* Let live 365 streams (nanocaster) piggyback on the icecast
1363                  * routine. They look very similar */
1364
1365                 p_sys->b_reconnect = true;
1366                 p_sys->b_pace_control = false;
1367                 p_sys->b_icecast = true;
1368             }
1369         }
1370         else if( !strcasecmp( psz, "Transfer-Encoding" ) )
1371         {
1372             msg_Dbg( p_access, "Transfer-Encoding: %s", p );
1373             if( !strncasecmp( p, "chunked", 7 ) )
1374             {
1375                 p_sys->b_chunked = true;
1376             }
1377         }
1378         else if( !strcasecmp( psz, "Icy-MetaInt" ) )
1379         {
1380             msg_Dbg( p_access, "Icy-MetaInt: %s", p );
1381             p_sys->i_icy_meta = atoi( p );
1382             if( p_sys->i_icy_meta < 0 )
1383                 p_sys->i_icy_meta = 0;
1384
1385             msg_Warn( p_access, "ICY metaint=%d", p_sys->i_icy_meta );
1386         }
1387         else if( !strcasecmp( psz, "Icy-Name" ) )
1388         {
1389             free( p_sys->psz_icy_name );
1390             p_sys->psz_icy_name = strdup( p );
1391             msg_Dbg( p_access, "Icy-Name: %s", p_sys->psz_icy_name );
1392
1393             p_sys->b_icecast = true; /* be on the safeside. set it here as well. */
1394             p_sys->b_reconnect = true;
1395             p_sys->b_pace_control = false;
1396         }
1397         else if( !strcasecmp( psz, "Icy-Genre" ) )
1398         {
1399             free( p_sys->psz_icy_genre );
1400             p_sys->psz_icy_genre = strdup( p );
1401             msg_Dbg( p_access, "Icy-Genre: %s", p_sys->psz_icy_genre );
1402         }
1403         else if( !strncasecmp( psz, "Icy-Notice", 10 ) )
1404         {
1405             msg_Dbg( p_access, "Icy-Notice: %s", p );
1406         }
1407         else if( !strncasecmp( psz, "icy-", 4 ) ||
1408                  !strncasecmp( psz, "ice-", 4 ) ||
1409                  !strncasecmp( psz, "x-audiocast", 11 ) )
1410         {
1411             msg_Dbg( p_access, "Meta-Info: %s: %s", psz, p );
1412         }
1413         else if( !strcasecmp( psz, "Set-Cookie" ) )
1414         {
1415             if( p_sys->cookies )
1416             {
1417                 msg_Dbg( p_access, "Accepting Cookie: %s", p );
1418                 cookie_append( p_sys->cookies, strdup(p) );
1419             }
1420             else
1421                 msg_Dbg( p_access, "We have a Cookie we won't remember: %s", p );
1422         }
1423         else if( !strcasecmp( psz, "www-authenticate" ) )
1424         {
1425             msg_Dbg( p_access, "Authentication header: %s", p );
1426             AuthParseHeader( p_access, p, &p_sys->auth );
1427         }
1428         else if( !strcasecmp( psz, "proxy-authenticate" ) )
1429         {
1430             msg_Dbg( p_access, "Proxy authentication header: %s", p );
1431             AuthParseHeader( p_access, p, &p_sys->proxy_auth );
1432         }
1433         else if( !strcasecmp( psz, "authentication-info" ) )
1434         {
1435             msg_Dbg( p_access, "Authentication Info header: %s", p );
1436             if( AuthCheckReply( p_access, p, &p_sys->url, &p_sys->auth ) )
1437                 goto error;
1438         }
1439         else if( !strcasecmp( psz, "proxy-authentication-info" ) )
1440         {
1441             msg_Dbg( p_access, "Proxy Authentication Info header: %s", p );
1442             if( AuthCheckReply( p_access, p, &p_sys->proxy, &p_sys->proxy_auth ) )
1443                 goto error;
1444         }
1445
1446         free( psz );
1447     }
1448     return VLC_SUCCESS;
1449
1450 error:
1451     Disconnect( p_access );
1452     return VLC_EGENERIC;
1453 }
1454
1455 /*****************************************************************************
1456  * Disconnect:
1457  *****************************************************************************/
1458 static void Disconnect( access_t *p_access )
1459 {
1460     access_sys_t *p_sys = p_access->p_sys;
1461
1462     if( p_sys->p_tls != NULL)
1463     {
1464         tls_ClientDelete( p_sys->p_tls );
1465         p_sys->p_tls = NULL;
1466         p_sys->p_vs = NULL;
1467     }
1468     if( p_sys->fd != -1)
1469     {
1470         net_Close(p_sys->fd);
1471         p_sys->fd = -1;
1472     }
1473
1474 }
1475
1476 /*****************************************************************************
1477  * Cookies (FIXME: we may want to rewrite that using a nice structure to hold
1478  * them) (FIXME: only support the "domain=" param)
1479  *****************************************************************************/
1480
1481 /* Get the NAME=VALUE part of the Cookie */
1482 static char * cookie_get_content( const char * cookie )
1483 {
1484     char * ret = strdup( cookie );
1485     if( !ret ) return NULL;
1486     char * str = ret;
1487     /* Look for a ';' */
1488     while( *str && *str != ';' ) str++;
1489     /* Replace it by a end-char */
1490     if( *str == ';' ) *str = 0;
1491     return ret;
1492 }
1493
1494 /* Get the domain where the cookie is stored */
1495 static char * cookie_get_domain( const char * cookie )
1496 {
1497     const char * str = cookie;
1498     static const char domain[] = "domain=";
1499     if( !str )
1500         return NULL;
1501     /* Look for a ';' */
1502     while( *str )
1503     {
1504         if( !strncmp( str, domain, sizeof(domain) - 1 /* minus \0 */ ) )
1505         {
1506             str += sizeof(domain) - 1 /* minus \0 */;
1507             char * ret = strdup( str );
1508             /* Now remove the next ';' if present */
1509             char * ret_iter = ret;
1510             while( *ret_iter && *ret_iter != ';' ) ret_iter++;
1511             if( *ret_iter == ';' )
1512                 *ret_iter = 0;
1513             return ret;
1514         }
1515         /* Go to next ';' field */
1516         while( *str && *str != ';' ) str++;
1517         if( *str == ';' ) str++;
1518         /* skip blank */
1519         while( *str && *str == ' ' ) str++;
1520     }
1521     return NULL;
1522 }
1523
1524 /* Get NAME in the NAME=VALUE field */
1525 static char * cookie_get_name( const char * cookie )
1526 {
1527     char * ret = cookie_get_content( cookie ); /* NAME=VALUE */
1528     if( !ret ) return NULL;
1529     char * str = ret;
1530     while( *str && *str != '=' ) str++;
1531     *str = 0;
1532     return ret;
1533 }
1534
1535 /* Add a cookie in cookies, checking to see how it should be added */
1536 static void cookie_append( vlc_array_t * cookies, char * cookie )
1537 {
1538     int i;
1539
1540     if( !cookie )
1541         return;
1542
1543     char * cookie_name = cookie_get_name( cookie );
1544
1545     /* Don't send invalid cookies */
1546     if( !cookie_name )
1547         return;
1548
1549     char * cookie_domain = cookie_get_domain( cookie );
1550     for( i = 0; i < vlc_array_count( cookies ); i++ )
1551     {
1552         char * current_cookie = vlc_array_item_at_index( cookies, i );
1553         char * current_cookie_name = cookie_get_name( current_cookie );
1554         char * current_cookie_domain = cookie_get_domain( current_cookie );
1555
1556         assert( current_cookie_name );
1557
1558         bool is_domain_matching = ( cookie_domain && current_cookie_domain &&
1559                                          !strcmp( cookie_domain, current_cookie_domain ) );
1560
1561         if( is_domain_matching && !strcmp( cookie_name, current_cookie_name )  )
1562         {
1563             /* Remove previous value for this cookie */
1564             free( current_cookie );
1565             vlc_array_remove( cookies, i );
1566
1567             /* Clean */
1568             free( current_cookie_name );
1569             free( current_cookie_domain );
1570             break;
1571         }
1572         free( current_cookie_name );
1573         free( current_cookie_domain );
1574     }
1575     free( cookie_name );
1576     free( cookie_domain );
1577     vlc_array_append( cookies, cookie );
1578 }
1579
1580 /*****************************************************************************
1581  * "RFC 2617: Basic and Digest Access Authentication" header parsing
1582  *****************************************************************************/
1583 static char *AuthGetParam( const char *psz_header, const char *psz_param )
1584 {
1585     char psz_what[strlen(psz_param)+3];
1586     sprintf( psz_what, "%s=\"", psz_param );
1587     psz_header = strstr( psz_header, psz_what );
1588     if( psz_header )
1589     {
1590         const char *psz_end;
1591         psz_header += strlen( psz_what );
1592         psz_end = strchr( psz_header, '"' );
1593         if( !psz_end ) /* Invalid since we should have a closing quote */
1594             return strdup( psz_header );
1595         return strndup( psz_header, psz_end - psz_header );
1596     }
1597     else
1598     {
1599         return NULL;
1600     }
1601 }
1602
1603 static char *AuthGetParamNoQuotes( const char *psz_header, const char *psz_param )
1604 {
1605     char psz_what[strlen(psz_param)+2];
1606     sprintf( psz_what, "%s=", psz_param );
1607     psz_header = strstr( psz_header, psz_what );
1608     if( psz_header )
1609     {
1610         const char *psz_end;
1611         psz_header += strlen( psz_what );
1612         psz_end = strchr( psz_header, ',' );
1613         /* XXX: Do we need to filter out trailing space between the value and
1614          * the comma/end of line? */
1615         if( !psz_end ) /* Can be valid if this is the last parameter */
1616             return strdup( psz_header );
1617         return strndup( psz_header, psz_end - psz_header );
1618     }
1619     else
1620     {
1621         return NULL;
1622     }
1623 }
1624
1625 static void AuthParseHeader( access_t *p_access, const char *psz_header,
1626                              http_auth_t *p_auth )
1627 {
1628     /* FIXME: multiple auth methods can be listed (comma seperated) */
1629
1630     /* 2 Basic Authentication Scheme */
1631     if( !strncasecmp( psz_header, "Basic ", strlen( "Basic " ) ) )
1632     {
1633         msg_Dbg( p_access, "Using Basic Authentication" );
1634         psz_header += strlen( "Basic " );
1635         p_auth->psz_realm = AuthGetParam( psz_header, "realm" );
1636         if( !p_auth->psz_realm )
1637             msg_Warn( p_access, "Basic Authentication: "
1638                       "Mandatory 'realm' parameter is missing" );
1639     }
1640     /* 3 Digest Access Authentication Scheme */
1641     else if( !strncasecmp( psz_header, "Digest ", strlen( "Digest " ) ) )
1642     {
1643         msg_Dbg( p_access, "Using Digest Access Authentication" );
1644         if( p_auth->psz_nonce ) return; /* FIXME */
1645         psz_header += strlen( "Digest " );
1646         p_auth->psz_realm = AuthGetParam( psz_header, "realm" );
1647         p_auth->psz_domain = AuthGetParam( psz_header, "domain" );
1648         p_auth->psz_nonce = AuthGetParam( psz_header, "nonce" );
1649         p_auth->psz_opaque = AuthGetParam( psz_header, "opaque" );
1650         p_auth->psz_stale = AuthGetParamNoQuotes( psz_header, "stale" );
1651         p_auth->psz_algorithm = AuthGetParamNoQuotes( psz_header, "algorithm" );
1652         p_auth->psz_qop = AuthGetParam( psz_header, "qop" );
1653         p_auth->i_nonce = 0;
1654         /* printf("realm: |%s|\ndomain: |%s|\nnonce: |%s|\nopaque: |%s|\n"
1655                   "stale: |%s|\nalgorithm: |%s|\nqop: |%s|\n",
1656                   p_auth->psz_realm,p_auth->psz_domain,p_auth->psz_nonce,
1657                   p_auth->psz_opaque,p_auth->psz_stale,p_auth->psz_algorithm,
1658                   p_auth->psz_qop); */
1659         if( !p_auth->psz_realm )
1660             msg_Warn( p_access, "Digest Access Authentication: "
1661                       "Mandatory 'realm' parameter is missing" );
1662         if( !p_auth->psz_nonce )
1663             msg_Warn( p_access, "Digest Access Authentication: "
1664                       "Mandatory 'nonce' parameter is missing" );
1665         if( p_auth->psz_qop ) /* FIXME: parse the qop list */
1666         {
1667             char *psz_tmp = strchr( p_auth->psz_qop, ',' );
1668             if( psz_tmp ) *psz_tmp = '\0';
1669         }
1670     }
1671     else
1672     {
1673         const char *psz_end = strchr( psz_header, ' ' );
1674         if( psz_end )
1675             msg_Warn( p_access, "Unknown authentication scheme: '%*s'",
1676                       psz_end - psz_header, psz_header );
1677         else
1678             msg_Warn( p_access, "Unknown authentication scheme: '%s'",
1679                       psz_header );
1680     }
1681 }
1682
1683 static char *AuthDigest( access_t *p_access, vlc_url_t *p_url,
1684                          http_auth_t *p_auth, const char *psz_method )
1685 {
1686     (void)p_access;
1687     const char *psz_username = p_url->psz_username ?: "";
1688     const char *psz_password = p_url->psz_password ?: "";
1689
1690     char *psz_HA1 = NULL;
1691     char *psz_HA2 = NULL;
1692     char *psz_response = NULL;
1693     struct md5_s md5;
1694
1695     /* H(A1) */
1696     if( p_auth->psz_HA1 )
1697     {
1698         psz_HA1 = strdup( p_auth->psz_HA1 );
1699         if( !psz_HA1 ) goto error;
1700     }
1701     else
1702     {
1703         InitMD5( &md5 );
1704         AddMD5( &md5, psz_username, strlen( psz_username ) );
1705         AddMD5( &md5, ":", 1 );
1706         AddMD5( &md5, p_auth->psz_realm, strlen( p_auth->psz_realm ) );
1707         AddMD5( &md5, ":", 1 );
1708         AddMD5( &md5, psz_password, strlen( psz_password ) );
1709         EndMD5( &md5 );
1710
1711         psz_HA1 = psz_md5_hash( &md5 );
1712         if( !psz_HA1 ) goto error;
1713
1714         if( p_auth->psz_algorithm
1715             && !strcmp( p_auth->psz_algorithm, "MD5-sess" ) )
1716         {
1717             InitMD5( &md5 );
1718             AddMD5( &md5, psz_HA1, 32 );
1719             free( psz_HA1 );
1720             AddMD5( &md5, ":", 1 );
1721             AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) );
1722             AddMD5( &md5, ":", 1 );
1723             AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) );
1724             EndMD5( &md5 );
1725
1726             psz_HA1 = psz_md5_hash( &md5 );
1727             if( !psz_HA1 ) goto error;
1728             p_auth->psz_HA1 = strdup( psz_HA1 );
1729             if( !p_auth->psz_HA1 ) goto error;
1730         }
1731     }
1732
1733     /* H(A2) */
1734     InitMD5( &md5 );
1735     if( *psz_method )
1736         AddMD5( &md5, psz_method, strlen( psz_method ) );
1737     AddMD5( &md5, ":", 1 );
1738     if( p_url->psz_path )
1739         AddMD5( &md5, p_url->psz_path, strlen( p_url->psz_path ) );
1740     else
1741         AddMD5( &md5, "/", 1 );
1742     if( p_auth->psz_qop && !strcmp( p_auth->psz_qop, "auth-int" ) )
1743     {
1744         char *psz_ent;
1745         struct md5_s ent;
1746         InitMD5( &ent );
1747         AddMD5( &ent, "", 0 ); /* XXX: entity-body. should be ok for GET */
1748         EndMD5( &ent );
1749         psz_ent = psz_md5_hash( &ent );
1750         if( !psz_ent ) goto error;
1751         AddMD5( &md5, ":", 1 );
1752         AddMD5( &md5, psz_ent, 32 );
1753         free( psz_ent );
1754     }
1755     EndMD5( &md5 );
1756     psz_HA2 = psz_md5_hash( &md5 );
1757     if( !psz_HA2 ) goto error;
1758
1759     /* Request digest */
1760     InitMD5( &md5 );
1761     AddMD5( &md5, psz_HA1, 32 );
1762     AddMD5( &md5, ":", 1 );
1763     AddMD5( &md5, p_auth->psz_nonce, strlen( p_auth->psz_nonce ) );
1764     AddMD5( &md5, ":", 1 );
1765     if( p_auth->psz_qop
1766         && ( !strcmp( p_auth->psz_qop, "auth" )
1767              || !strcmp( p_auth->psz_qop, "auth-int" ) ) )
1768     {
1769         char psz_inonce[9];
1770         snprintf( psz_inonce, 9, "%08x", p_auth->i_nonce );
1771         AddMD5( &md5, psz_inonce, 8 );
1772         AddMD5( &md5, ":", 1 );
1773         AddMD5( &md5, p_auth->psz_cnonce, strlen( p_auth->psz_cnonce ) );
1774         AddMD5( &md5, ":", 1 );
1775         AddMD5( &md5, p_auth->psz_qop, strlen( p_auth->psz_qop ) );
1776         AddMD5( &md5, ":", 1 );
1777     }
1778     AddMD5( &md5, psz_HA2, 32 );
1779     EndMD5( &md5 );
1780     psz_response = psz_md5_hash( &md5 );
1781
1782     error:
1783         free( psz_HA1 );
1784         free( psz_HA2 );
1785         return psz_response;
1786 }
1787
1788
1789 static void AuthReply( access_t *p_access, const char *psz_prefix,
1790                        vlc_url_t *p_url, http_auth_t *p_auth )
1791 {
1792     access_sys_t *p_sys = p_access->p_sys;
1793     v_socket_t     *pvs = p_sys->p_vs;
1794
1795     const char *psz_username = p_url->psz_username ?: "";
1796     const char *psz_password = p_url->psz_password ?: "";
1797
1798     if( p_auth->psz_nonce )
1799     {
1800         /* Digest Access Authentication */
1801         char *psz_response;
1802
1803         if(    p_auth->psz_algorithm
1804             && strcmp( p_auth->psz_algorithm, "MD5" )
1805             && strcmp( p_auth->psz_algorithm, "MD5-sess" ) )
1806         {
1807             msg_Err( p_access, "Digest Access Authentication: "
1808                      "Unknown algorithm '%s'", p_auth->psz_algorithm );
1809             return;
1810         }
1811
1812         if( p_auth->psz_qop || !p_auth->psz_cnonce )
1813         {
1814             /* FIXME: needs to be really random to prevent man in the middle
1815              * attacks */
1816             free( p_auth->psz_cnonce );
1817             p_auth->psz_cnonce = strdup( "Some random string FIXME" );
1818         }
1819         p_auth->i_nonce ++;
1820
1821         psz_response = AuthDigest( p_access, p_url, p_auth, "GET" );
1822         if( !psz_response ) return;
1823
1824         net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1825                     "%sAuthorization: Digest "
1826                     /* Mandatory parameters */
1827                     "username=\"%s\", "
1828                     "realm=\"%s\", "
1829                     "nonce=\"%s\", "
1830                     "uri=\"%s\", "
1831                     "response=\"%s\", "
1832                     /* Optional parameters */
1833                     "%s%s%s" /* algorithm */
1834                     "%s%s%s" /* cnonce */
1835                     "%s%s%s" /* opaque */
1836                     "%s%s%s" /* message qop */
1837                     "%s%08x%s" /* nonce count */
1838                     "\r\n",
1839                     /* Mandatory parameters */
1840                     psz_prefix,
1841                     psz_username,
1842                     p_auth->psz_realm,
1843                     p_auth->psz_nonce,
1844                     p_url->psz_path ?: "/",
1845                     psz_response,
1846                     /* Optional parameters */
1847                     p_auth->psz_algorithm ? "algorithm=\"" : "",
1848                     p_auth->psz_algorithm ?: "",
1849                     p_auth->psz_algorithm ? "\", " : "",
1850                     p_auth->psz_cnonce ? "cnonce=\"" : "",
1851                     p_auth->psz_cnonce ?: "",
1852                     p_auth->psz_cnonce ? "\", " : "",
1853                     p_auth->psz_opaque ? "opaque=\"" : "",
1854                     p_auth->psz_opaque ?: "",
1855                     p_auth->psz_opaque ? "\", " : "",
1856                     p_auth->psz_qop ? "qop=\"" : "",
1857                     p_auth->psz_qop ?: "",
1858                     p_auth->psz_qop ? "\", " : "",
1859                     p_auth->i_nonce ? "nc=\"" : "uglyhack=\"", /* Will be parsed as an unhandled extension */
1860                     p_auth->i_nonce,
1861                     p_auth->i_nonce ? "\"" : "\""
1862                   );
1863
1864         free( psz_response );
1865     }
1866     else
1867     {
1868         /* Basic Access Authentication */
1869         char buf[strlen( psz_username ) + strlen( psz_password ) + 2];
1870         char *b64;
1871
1872         snprintf( buf, sizeof( buf ), "%s:%s", psz_username, psz_password );
1873         b64 = vlc_b64_encode( buf );
1874
1875         if( b64 != NULL )
1876         {
1877              net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
1878                          "%sAuthorization: Basic %s\r\n", psz_prefix, b64 );
1879              free( b64 );
1880         }
1881     }
1882 }
1883
1884 static int AuthCheckReply( access_t *p_access, const char *psz_header,
1885                            vlc_url_t *p_url, http_auth_t *p_auth )
1886 {
1887     int i_ret = VLC_EGENERIC;
1888     char *psz_nextnonce = AuthGetParam( psz_header, "nextnonce" );
1889     char *psz_qop = AuthGetParamNoQuotes( psz_header, "qop" );
1890     char *psz_rspauth = AuthGetParam( psz_header, "rspauth" );
1891     char *psz_cnonce = AuthGetParam( psz_header, "cnonce" );
1892     char *psz_nc = AuthGetParamNoQuotes( psz_header, "nc" );
1893
1894     if( psz_cnonce )
1895     {
1896         char *psz_digest;
1897
1898         if( strcmp( psz_cnonce, p_auth->psz_cnonce ) )
1899         {
1900             msg_Err( p_access, "HTTP Digest Access Authentication: server replied with a different client nonce value." );
1901             goto error;
1902         }
1903
1904         if( psz_nc )
1905         {
1906             int i_nonce;
1907             i_nonce = strtol( psz_nc, NULL, 16 );
1908             if( i_nonce != p_auth->i_nonce )
1909             {
1910                 msg_Err( p_access, "HTTP Digest Access Authentication: server replied with a different nonce count value." );
1911                 goto error;
1912             }
1913         }
1914
1915         if( psz_qop && p_auth->psz_qop && strcmp( psz_qop, p_auth->psz_qop ) )
1916             msg_Warn( p_access, "HTTP Digest Access Authentication: server replied using a different 'quality of protection' option" );
1917
1918         /* All the clear text values match, let's now check the response
1919          * digest */
1920         psz_digest = AuthDigest( p_access, p_url, p_auth, "" );
1921         if( strcmp( psz_digest, psz_rspauth ) )
1922         {
1923             msg_Err( p_access, "HTTP Digest Access Authentication: server replied with an invalid response digest (expected value: %s).", psz_digest );
1924             free( psz_digest );
1925             goto error;
1926         }
1927         free( psz_digest );
1928     }
1929
1930     if( psz_nextnonce )
1931     {
1932         free( p_auth->psz_nonce );
1933         p_auth->psz_nonce = psz_nextnonce;
1934         psz_nextnonce = NULL;
1935     }
1936
1937     i_ret = VLC_SUCCESS;
1938     error:
1939         free( psz_nextnonce );
1940         free( psz_qop );
1941         free( psz_rspauth );
1942         free( psz_cnonce );
1943         free( psz_nc );
1944
1945     return i_ret;
1946 }
1947
1948 static void AuthReset( http_auth_t *p_auth )
1949 {
1950     FREENULL( p_auth->psz_realm );
1951     FREENULL( p_auth->psz_domain );
1952     FREENULL( p_auth->psz_nonce );
1953     FREENULL( p_auth->psz_opaque );
1954     FREENULL( p_auth->psz_stale );
1955     FREENULL( p_auth->psz_algorithm );
1956     FREENULL( p_auth->psz_qop );
1957     p_auth->i_nonce = 0;
1958     FREENULL( p_auth->psz_cnonce );
1959     FREENULL( p_auth->psz_HA1 );
1960 }