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