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