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