dvb: don't preparse
[vlc.git] / modules / access / http.c
1 /*****************************************************************************
2  * http.c: HTTP input module
3  *****************************************************************************
4  * Copyright (C) 2001-2008 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Rémi Denis-Courmont <rem # videolan.org>
10  *          Antoine Cellerier <dionoea at videolan dot org>
11  *
12  * This program is free software; you can redistribute it and/or modify it
13  * under the terms of the GNU Lesser General Public License as published by
14  * the Free Software Foundation; either version 2.1 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public License
23  * along with this program; if not, write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <errno.h>
35
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38 #include <vlc_access.h>
39 #include <vlc_meta.h>
40 #include <vlc_network.h>
41 #include <vlc_url.h>
42 #include <vlc_tls.h>
43 #include <vlc_strings.h>
44 #include <vlc_charset.h>
45 #include <vlc_input.h>
46 #include <vlc_http.h>
47 #include <vlc_interrupt.h>
48 #include <vlc_keystore.h>
49
50 #include <assert.h>
51 #include <limits.h>
52
53 /*****************************************************************************
54  * Module descriptor
55  *****************************************************************************/
56 static int  Open ( vlc_object_t * );
57 static void Close( vlc_object_t * );
58
59 #define PROXY_TEXT N_("HTTP proxy")
60 #define PROXY_LONGTEXT N_( \
61     "HTTP proxy to be used It must be of the form " \
62     "http://[user@]myproxy.mydomain:myport/ ; " \
63     "if empty, the http_proxy environment variable will be tried." )
64
65 #define PROXY_PASS_TEXT N_("HTTP proxy password")
66 #define PROXY_PASS_LONGTEXT N_( \
67     "If your HTTP proxy requires a password, set it here." )
68
69 #define RECONNECT_TEXT N_("Auto re-connect")
70 #define RECONNECT_LONGTEXT N_( \
71     "Automatically try to reconnect to the stream in case of a sudden " \
72     "disconnect." )
73
74 #define CONTINUOUS_TEXT N_("Continuous stream")
75 #define CONTINUOUS_LONGTEXT N_("Read a file that is " \
76     "being constantly updated (for example, a JPG file on a server). " \
77     "You should not globally enable this option as it will break all other " \
78     "types of HTTP streams." )
79
80 #define FORWARD_COOKIES_TEXT N_("Forward Cookies")
81 #define FORWARD_COOKIES_LONGTEXT N_("Forward Cookies across http redirections.")
82
83 #define REFERER_TEXT N_("HTTP referer value")
84 #define REFERER_LONGTEXT N_("Customize the HTTP referer, simulating a previous document")
85
86 #define UA_TEXT N_("User Agent")
87 #define UA_LONGTEXT N_("The name and version of the program will be " \
88     "provided to the HTTP server. They must be separated by a forward " \
89     "slash, e.g. FooBar/1.2.3. This option can only be specified per input " \
90     "item, not globally.")
91
92 vlc_module_begin ()
93     set_description( N_("HTTP input") )
94     set_capability( "access", 0 )
95     set_shortname( N_( "HTTP(S)" ) )
96     set_category( CAT_INPUT )
97     set_subcategory( SUBCAT_INPUT_ACCESS )
98
99     add_string( "http-proxy", NULL, PROXY_TEXT, PROXY_LONGTEXT,
100                 false )
101     add_password( "http-proxy-pwd", NULL,
102                   PROXY_PASS_TEXT, PROXY_PASS_LONGTEXT, false )
103     add_obsolete_bool( "http-use-IE-proxy" )
104     add_string( "http-referrer", NULL, REFERER_TEXT, REFERER_LONGTEXT, false )
105         change_safe()
106     add_string( "http-user-agent", NULL, UA_TEXT, UA_LONGTEXT, false )
107         change_safe()
108         change_private()
109     add_bool( "http-reconnect", false, RECONNECT_TEXT,
110               RECONNECT_LONGTEXT, true )
111     add_bool( "http-continuous", false, CONTINUOUS_TEXT,
112               CONTINUOUS_LONGTEXT, true )
113         change_safe()
114     add_bool( "http-forward-cookies", true, FORWARD_COOKIES_TEXT,
115               FORWARD_COOKIES_LONGTEXT, true )
116     /* 'itpc' = iTunes Podcast */
117     add_shortcut( "http", "https", "unsv", "itpc", "icyx" )
118     set_callbacks( Open, Close )
119 vlc_module_end ()
120
121 /*****************************************************************************
122  * Local prototypes
123  *****************************************************************************/
124
125 struct access_sys_t
126 {
127     int fd;
128     vlc_tls_creds_t *p_creds;
129     vlc_tls_t *p_tls;
130
131     /* From uri */
132     vlc_url_t url;
133     char    *psz_user_agent;
134     char    *psz_referrer;
135     char    *psz_username;
136     char    *psz_password;
137     vlc_http_auth_t auth;
138
139     /* Proxy */
140     bool b_proxy;
141     vlc_url_t  proxy;
142     vlc_http_auth_t proxy_auth;
143     char       *psz_proxy_passbuf;
144
145     /* */
146     int        i_code;
147
148     char       *psz_mime;
149     char       *psz_location;
150     bool b_mms;
151     bool b_icecast;
152
153     bool b_chunked;
154     int64_t    i_chunk;
155
156     int        i_icy_meta;
157     uint64_t   i_icy_offset;
158     char       *psz_icy_name;
159     char       *psz_icy_genre;
160     char       *psz_icy_title;
161
162     uint64_t i_remaining;
163     uint64_t offset;
164     uint64_t size;
165
166     /* cookie jar borrowed from playlist, do not free */
167     vlc_http_cookie_jar_t * cookies;
168
169     bool b_seekable;
170     bool b_reconnect;
171     bool b_continuous;
172     bool b_pace_control;
173     bool b_has_size;
174 };
175
176 /* */
177 static ssize_t Read( access_t *, uint8_t *, size_t );
178 static int Seek( access_t *, uint64_t );
179 static int Control( access_t *, int, va_list );
180
181 /* */
182 static int Connect( access_t *, uint64_t );
183 static int Request( access_t *p_access, uint64_t i_tell );
184 static void Disconnect( access_t * );
185
186
187 static void AuthReply( access_t *p_acces, const char *psz_prefix,
188                        vlc_url_t *p_url, vlc_http_auth_t *p_auth );
189 static int AuthCheckReply( access_t *p_access, const char *psz_header,
190                            vlc_url_t *p_url, vlc_http_auth_t *p_auth );
191 static vlc_http_cookie_jar_t *GetCookieJar( vlc_object_t *p_this );
192
193 /*****************************************************************************
194  * Open:
195  *****************************************************************************/
196 static int Open( vlc_object_t *p_this )
197 {
198     access_t *p_access = (access_t*)p_this;
199     const char *psz_url = p_access->psz_url;
200     char *psz;
201     int ret = VLC_EGENERIC;
202     vlc_credential credential;
203
204     access_sys_t *p_sys = malloc( sizeof(*p_sys) );
205     if( unlikely(p_sys == NULL) )
206         return VLC_ENOMEM;
207
208     p_sys->fd = -1;
209     p_sys->b_proxy = false;
210     p_sys->psz_proxy_passbuf = NULL;
211     p_sys->b_seekable = true;
212     p_sys->psz_mime = NULL;
213     p_sys->b_mms = false;
214     p_sys->b_icecast = false;
215     p_sys->psz_location = NULL;
216     p_sys->psz_user_agent = NULL;
217     p_sys->psz_referrer = NULL;
218     p_sys->psz_username = NULL;
219     p_sys->psz_password = NULL;
220     p_sys->b_pace_control = true;
221     p_sys->p_creds = NULL;
222     p_sys->p_tls = NULL;
223     p_sys->i_icy_meta = 0;
224     p_sys->i_icy_offset = 0;
225     p_sys->psz_icy_name = NULL;
226     p_sys->psz_icy_genre = NULL;
227     p_sys->psz_icy_title = NULL;
228     p_sys->i_remaining = 0;
229     p_sys->b_has_size = false;
230     p_sys->offset = 0;
231     p_sys->size = 0;
232     p_access->info.b_eof  = false;
233     p_access->p_sys = p_sys;
234
235     /* Only forward an store cookies if the corresponding option is activated */
236     if( var_CreateGetBool( p_access, "http-forward-cookies" ) )
237         p_sys->cookies = GetCookieJar( p_this );
238     else
239         p_sys->cookies = NULL;
240
241     vlc_http_auth_Init( &p_sys->auth );
242     vlc_http_auth_Init( &p_sys->proxy_auth );
243     vlc_UrlParse( &p_sys->url, psz_url );
244     vlc_credential_init( &credential, &p_sys->url );
245
246     if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
247     {
248         msg_Warn( p_access, "invalid host" );
249         goto error;
250     }
251     if( !strcmp( p_sys->url.psz_protocol, "https" ) )
252     {
253         /* HTTP over SSL */
254         p_sys->p_creds = vlc_tls_ClientCreate( p_this );
255         if( p_sys->p_creds == NULL )
256             goto error;
257         if( p_sys->url.i_port <= 0 )
258             p_sys->url.i_port = 443;
259     }
260     else
261     {
262         if( p_sys->url.i_port <= 0 )
263             p_sys->url.i_port = 80;
264     }
265
266     /* Determine the HTTP user agent */
267     /* See RFC2616 §2.2 token and comment definition, and §3.8 and
268      * §14.43 user-agent header */
269     p_sys->psz_user_agent = var_InheritString( p_access, "http-user-agent" );
270     if (p_sys->psz_user_agent)
271     {
272         unsigned comment_level = 0;
273         for( char *p = p_sys->psz_user_agent; *p; p++ )
274         {
275             uint8_t c = *p;
276             if (comment_level == 0)
277             {
278                 if( c < 32 || strchr( ")<>@,;:\\\"[]?={}", c ) )
279                     *p = '_'; /* remove potentially harmful characters */
280             }
281             else
282             {
283                 if (c == ')')
284                     comment_level--;
285                 else if( c < 32 && strchr( "\t\r\n", c ) == NULL)
286                     *p = '_'; /* remove potentially harmful characters */
287             }
288             if (c == '(')
289             {
290                 if (comment_level == UINT_MAX)
291                     break;
292                 comment_level++;
293             }
294         }
295         /* truncate evil unclosed comments */
296         if (comment_level > 0)
297         {
298             char *p = strchr(p_sys->psz_user_agent, '(');
299             *p = '\0';
300         }
301     }
302
303     /* HTTP referrer */
304     p_sys->psz_referrer = var_InheritString( p_access, "http-referrer" );
305
306     /* Check proxy */
307     psz = var_InheritString( p_access, "http-proxy" );
308     if( psz == NULL )
309     {
310         msg_Dbg(p_access, "querying proxy for %s", psz_url);
311         psz = vlc_getProxyUrl(psz_url);
312
313         if (psz != NULL)
314             msg_Dbg(p_access, "proxy: %s", psz);
315         else
316             msg_Dbg(p_access, "no proxy");
317     }
318     if( psz != NULL )
319     {
320         p_sys->b_proxy = true;
321         vlc_UrlParse( &p_sys->proxy, psz );
322         free( psz );
323
324         psz = var_InheritString( p_access, "http-proxy-pwd" );
325         if( psz )
326             p_sys->proxy.psz_password = p_sys->psz_proxy_passbuf = psz;
327
328         if( p_sys->proxy.psz_host == NULL || *p_sys->proxy.psz_host == '\0' )
329         {
330             msg_Warn( p_access, "invalid proxy host" );
331             goto error;
332         }
333         if( p_sys->proxy.i_port <= 0 )
334         {
335             p_sys->proxy.i_port = 80;
336         }
337     }
338
339     msg_Dbg( p_access, "http: server='%s' port=%d file='%s'",
340              p_sys->url.psz_host, p_sys->url.i_port,
341              p_sys->url.psz_path != NULL ? p_sys->url.psz_path : "" );
342     if( p_sys->b_proxy )
343     {
344         msg_Dbg( p_access, "      proxy %s:%d", p_sys->proxy.psz_host,
345                  p_sys->proxy.i_port );
346     }
347     if( p_sys->url.psz_username && *p_sys->url.psz_username )
348     {
349         msg_Dbg( p_access, "      user='%s'", p_sys->url.psz_username );
350     }
351
352     p_sys->b_reconnect = var_InheritBool( p_access, "http-reconnect" );
353     p_sys->b_continuous = var_InheritBool( p_access, "http-continuous" );
354
355     if( vlc_credential_get( &credential, p_access, NULL, NULL, NULL, NULL ) )
356     {
357         p_sys->url.psz_username = (char *) credential.psz_username;
358         p_sys->url.psz_password = (char *) credential.psz_password;
359     }
360
361 connect:
362     /* Connect */
363     if( Connect( p_access, 0 ) )
364         goto error;
365
366     if( p_sys->i_code == 401 )
367     {
368         if( p_sys->auth.psz_realm == NULL )
369         {
370             msg_Err( p_access, "authentication failed without realm" );
371             goto error;
372         }
373         /* FIXME ? */
374         if( p_sys->url.psz_username && p_sys->url.psz_password &&
375             p_sys->auth.psz_nonce && p_sys->auth.i_nonce == 0 )
376         {
377             Disconnect( p_access );
378             goto connect;
379         }
380         free( p_sys->psz_username );
381         free( p_sys->psz_password );
382         p_sys->psz_username = p_sys->psz_password = NULL;
383
384         msg_Dbg( p_access, "authentication failed for realm %s",
385                  p_sys->auth.psz_realm );
386
387         credential.psz_realm = p_sys->auth.psz_realm;
388         credential.psz_authtype = p_sys->auth.psz_nonce  ? "Digest" : "Basic";
389
390         if( vlc_credential_get( &credential, p_access, NULL, NULL,
391                                _("HTTP authentication"),
392                                _("Please enter a valid login name and a "
393                                "password for realm %s."), p_sys->auth.psz_realm ) )
394         {
395             p_sys->psz_username = strdup(credential.psz_username);
396             p_sys->psz_password = strdup(credential.psz_password);
397             if (!p_sys->psz_username || !p_sys->psz_password)
398                 goto error;
399             msg_Err( p_access, "retrying with user=%s", p_sys->psz_username );
400             p_sys->url.psz_username = p_sys->psz_username;
401             p_sys->url.psz_password = p_sys->psz_password;
402             Disconnect( p_access );
403             goto connect;
404         }
405         else
406             goto error;
407     }
408     else
409         vlc_credential_store( &credential, p_access );
410
411     if( ( p_sys->i_code == 301 || p_sys->i_code == 302 ||
412           p_sys->i_code == 303 || p_sys->i_code == 307 ) &&
413         p_sys->psz_location != NULL )
414     {
415         p_access->psz_url = p_sys->psz_location;
416         p_sys->psz_location = NULL;
417         ret = VLC_ACCESS_REDIRECT;
418         goto error;
419     }
420
421     if( p_sys->p_creds == NULL && p_sys->b_mms )
422     {
423         msg_Dbg( p_access, "redirecting to MMS plug-in" );
424
425         if( unlikely(asprintf( &p_access->psz_url, "mmsh://%s",
426                                p_access->psz_location ) == -1) )
427             p_access->psz_url = NULL;
428         ret = VLC_ACCESS_REDIRECT;
429         goto error;
430     }
431
432     if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );
433
434     /* Set up p_access */
435     p_access->pf_read = Read;
436     p_access->pf_control = Control;
437     p_access->pf_seek = Seek;
438
439     vlc_credential_clean( &credential );
440
441     return VLC_SUCCESS;
442
443 error:
444     vlc_credential_clean( &credential );
445     vlc_UrlClean( &p_sys->url );
446     if( p_sys->b_proxy )
447         vlc_UrlClean( &p_sys->proxy );
448     free( p_sys->psz_proxy_passbuf );
449     free( p_sys->psz_mime );
450     free( p_sys->psz_location );
451     free( p_sys->psz_user_agent );
452     free( p_sys->psz_referrer );
453     free( p_sys->psz_username );
454     free( p_sys->psz_password );
455
456     Disconnect( p_access );
457     vlc_tls_Delete( p_sys->p_creds );
458
459     free( p_sys );
460     return ret;
461 }
462
463 /*****************************************************************************
464  * Close:
465  *****************************************************************************/
466 static void Close( vlc_object_t *p_this )
467 {
468     access_t     *p_access = (access_t*)p_this;
469     access_sys_t *p_sys = p_access->p_sys;
470
471     vlc_UrlClean( &p_sys->url );
472     vlc_http_auth_Deinit( &p_sys->auth );
473     if( p_sys->b_proxy )
474         vlc_UrlClean( &p_sys->proxy );
475     vlc_http_auth_Deinit( &p_sys->proxy_auth );
476
477     free( p_sys->psz_mime );
478     free( p_sys->psz_location );
479
480     free( p_sys->psz_icy_name );
481     free( p_sys->psz_icy_genre );
482     free( p_sys->psz_icy_title );
483
484     free( p_sys->psz_user_agent );
485     free( p_sys->psz_referrer );
486     free( p_sys->psz_username );
487     free( p_sys->psz_password );
488
489     Disconnect( p_access );
490     vlc_tls_Delete( p_sys->p_creds );
491
492     free( p_sys );
493 }
494
495 /* Read data from the socket taking care of chunked transfer if needed */
496 static int ReadData( access_t *p_access, int *pi_read,
497                      uint8_t *p_buffer, size_t i_len )
498 {
499     access_sys_t *p_sys = p_access->p_sys;
500     if( p_sys->b_chunked )
501     {
502         if( p_sys->i_chunk < 0 )
503             return VLC_EGENERIC;
504
505         if( p_sys->i_chunk <= 0 )
506         {
507             char *psz;
508             if( p_sys->p_tls != NULL )
509                 psz = vlc_tls_GetLine( p_sys->p_tls );
510             else
511                 psz = net_Gets( p_access, p_sys->fd );
512             /* read the chunk header */
513             if( psz == NULL )
514             {
515                 /* fatal error - end of file */
516                 msg_Dbg( p_access, "failed reading chunk-header line" );
517                 return VLC_EGENERIC;
518             }
519             p_sys->i_chunk = strtoll( psz, NULL, 16 );
520             free( psz );
521
522             if( p_sys->i_chunk <= 0 )   /* eof */
523             {
524                 p_sys->i_chunk = -1;
525                 return VLC_EGENERIC;
526             }
527         }
528
529         if( i_len > p_sys->i_chunk )
530             i_len = p_sys->i_chunk;
531     }
532
533     if( p_sys->p_tls != NULL )
534         *pi_read = vlc_tls_Read( p_sys->p_tls, p_buffer, i_len, false );
535     else
536         *pi_read = vlc_recv_i11e( p_sys->fd, p_buffer, i_len, 0 );
537     if( *pi_read < 0 && errno != EINTR && errno != EAGAIN )
538         return VLC_EGENERIC;
539     if( *pi_read <= 0 )
540         return VLC_SUCCESS;
541
542     if( p_sys->b_chunked )
543     {
544         p_sys->i_chunk -= *pi_read;
545         if( p_sys->i_chunk <= 0 )
546         {   /* read the empty line */
547             if( p_sys->p_tls != NULL )
548                 free( vlc_tls_GetLine( p_sys->p_tls ) );
549             else
550                 free( net_Gets( p_access, p_sys->fd ) );
551         }
552     }
553     return VLC_SUCCESS;
554 }
555
556 /*****************************************************************************
557  * Read: Read up to i_len bytes from the http connection and place in
558  * p_buffer. Return the actual number of bytes read
559  *****************************************************************************/
560 static int ReadICYMeta( access_t *p_access );
561 static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
562 {
563     access_sys_t *p_sys = p_access->p_sys;
564     int i_read;
565
566     if( p_sys->fd == -1 )
567         goto fatal;
568
569     if( p_sys->b_has_size )
570     {
571         /* Remaining bytes in the file */
572         uint64_t remainder = p_sys->size - p_sys->offset;
573         if( remainder < i_len )
574             i_len = remainder;
575
576         /* Remaining bytes in the response */
577         if( p_sys->i_remaining < i_len )
578             i_len = p_sys->i_remaining;
579     }
580     if( i_len == 0 )
581         goto fatal;
582
583     if( p_sys->i_icy_meta > 0 && p_sys->offset - p_sys->i_icy_offset > 0 )
584     {
585         int64_t i_next = p_sys->i_icy_meta -
586                                     (p_sys->offset - p_sys->i_icy_offset ) % p_sys->i_icy_meta;
587
588         if( i_next == p_sys->i_icy_meta )
589         {
590             if( ReadICYMeta( p_access ) )
591                 goto fatal;
592         }
593         if( i_len > i_next )
594             i_len = i_next;
595     }
596
597     if( ReadData( p_access, &i_read, p_buffer, i_len ) )
598         goto fatal;
599
600     if( i_read < 0 )
601         return -1; /* EINTR / EAGAIN */
602
603     if( i_read == 0 )
604     {
605         Disconnect( p_access );
606         if( p_sys->b_reconnect )
607         {
608             msg_Dbg( p_access, "got disconnected, trying to reconnect" );
609             if( Connect( p_access, p_sys->offset ) )
610                 msg_Dbg( p_access, "reconnection failed" );
611             else
612                 return -1;
613         }
614         goto fatal;
615     }
616
617     assert( i_read >= 0 );
618     p_sys->offset += i_read;
619     if( p_sys->b_has_size )
620     {
621         assert( p_sys->offset <= p_sys->size );
622         assert( (unsigned)i_read <= p_sys->i_remaining );
623         p_sys->i_remaining -= i_read;
624     }
625
626     return i_read;
627
628 fatal:
629     p_access->info.b_eof = true;
630     return 0;
631 }
632
633 static int ReadICYMeta( access_t *p_access )
634 {
635     access_sys_t *p_sys = p_access->p_sys;
636
637     uint8_t buffer;
638     char *p, *psz_meta;
639     int i_read;
640
641     /* Read meta data length */
642     if( ReadData( p_access, &i_read, &buffer, 1 ) )
643         return VLC_EGENERIC;
644     if( i_read != 1 )
645         return VLC_EGENERIC;
646     const int i_size = buffer << 4;
647     /* msg_Dbg( p_access, "ICY meta size=%u", i_size); */
648
649     psz_meta = malloc( i_size + 1 );
650     for( i_read = 0; i_read < i_size; )
651     {
652         int i_tmp;
653         if( ReadData( p_access, &i_tmp, (uint8_t *)&psz_meta[i_read], i_size - i_read ) || i_tmp <= 0 )
654         {
655             free( psz_meta );
656             return VLC_EGENERIC;
657         }
658         i_read += i_tmp;
659     }
660     psz_meta[i_read] = '\0'; /* Just in case */
661
662     /* msg_Dbg( p_access, "icy-meta=%s", psz_meta ); */
663
664     /* Now parse the meta */
665     /* Look for StreamTitle= */
666     p = strcasestr( (char *)psz_meta, "StreamTitle=" );
667     if( p )
668     {
669         p += strlen( "StreamTitle=" );
670         if( *p == '\'' || *p == '"' )
671         {
672             char closing[] = { p[0], ';', '\0' };
673             char *psz = strstr( &p[1], closing );
674             if( !psz )
675                 psz = strchr( &p[1], ';' );
676
677             if( psz ) *psz = '\0';
678         }
679         else
680         {
681             char *psz = strchr( &p[1], ';' );
682             if( psz ) *psz = '\0';
683         }
684
685         if( !p_sys->psz_icy_title ||
686             strcmp( p_sys->psz_icy_title, &p[1] ) )
687         {
688             free( p_sys->psz_icy_title );
689             char *psz_tmp = strdup( &p[1] );
690             p_sys->psz_icy_title = EnsureUTF8( psz_tmp );
691             if( !p_sys->psz_icy_title )
692                 free( psz_tmp );
693
694             msg_Dbg( p_access, "New Icy-Title=%s", p_sys->psz_icy_title );
695             input_thread_t *p_input = p_access->p_input;
696             if( p_input )
697             {
698                 input_item_t *p_input_item = input_GetItem( p_access->p_input );
699                 if( p_input_item )
700                     input_item_SetMeta( p_input_item, vlc_meta_NowPlaying, p_sys->psz_icy_title );
701             }
702         }
703     }
704     free( psz_meta );
705
706     return VLC_SUCCESS;
707 }
708
709 /*****************************************************************************
710  * Seek: close and re-open a connection at the right place
711  *****************************************************************************/
712 static int Seek( access_t *p_access, uint64_t i_pos )
713 {
714     access_sys_t *p_sys = p_access->p_sys;
715
716     msg_Dbg( p_access, "trying to seek to %"PRId64, i_pos );
717     Disconnect( p_access );
718
719     if( p_sys->size && i_pos >= p_sys->size )
720     {
721         msg_Err( p_access, "seek too far" );
722         int retval = Seek( p_access, p_sys->size - 1 );
723         if( retval == VLC_SUCCESS ) {
724             uint8_t p_buffer[2];
725             Read( p_access, p_buffer, 1);
726             p_access->info.b_eof  = false;
727         }
728         return retval;
729     }
730     if( Connect( p_access, i_pos ) )
731     {
732         msg_Err( p_access, "seek failed" );
733         p_access->info.b_eof = true;
734         return VLC_EGENERIC;
735     }
736     return VLC_SUCCESS;
737 }
738
739 /*****************************************************************************
740  * Control:
741  *****************************************************************************/
742 static int Control( access_t *p_access, int i_query, va_list args )
743 {
744     access_sys_t *p_sys = p_access->p_sys;
745     bool       *pb_bool;
746     int64_t    *pi_64;
747
748     switch( i_query )
749     {
750         /* */
751         case ACCESS_CAN_SEEK:
752             pb_bool = (bool*)va_arg( args, bool* );
753             *pb_bool = p_sys->b_seekable;
754             break;
755         case ACCESS_CAN_FASTSEEK:
756             pb_bool = (bool*)va_arg( args, bool* );
757             *pb_bool = false;
758             break;
759         case ACCESS_CAN_PAUSE:
760         case ACCESS_CAN_CONTROL_PACE:
761             pb_bool = (bool*)va_arg( args, bool* );
762
763 #if 0       /* Disable for now until we have a clock synchro algo
764              * which works with something else than MPEG over UDP */
765             *pb_bool = p_sys->b_pace_control;
766 #endif
767             *pb_bool = true;
768             break;
769
770         /* */
771         case ACCESS_GET_PTS_DELAY:
772             pi_64 = (int64_t*)va_arg( args, int64_t * );
773             *pi_64 = INT64_C(1000)
774                 * var_InheritInteger( p_access, "network-caching" );
775             break;
776
777         case ACCESS_GET_SIZE:
778             if( !p_sys->b_has_size )
779                 return VLC_EGENERIC;
780             pi_64 = (int64_t*)va_arg( args, int64_t * );
781             *pi_64 = p_sys->size;
782            break;
783
784         /* */
785         case ACCESS_SET_PAUSE_STATE:
786             break;
787
788         case ACCESS_GET_CONTENT_TYPE:
789         {
790             char **type = va_arg( args, char ** );
791
792             if( p_sys->b_icecast && p_sys->psz_mime == NULL )
793                 *type = strdup( "audio/mpeg" );
794             else if( !strcasecmp( p_access->psz_access, "itpc" ) )
795                 *type = strdup( "application/rss+xml" );
796             else if( !strcasecmp( p_access->psz_access, "unsv" ) &&
797                 p_sys->psz_mime != NULL &&
798                 !strcasecmp( p_sys->psz_mime, "misc/ultravox" ) )
799                 /* Grrrr! detect ultravox server and force NSV demuxer */
800                 *type = strdup( "video/nsa" );
801             else if( p_sys->psz_mime )
802                 *type = strdup( p_sys->psz_mime );
803             else
804                 return VLC_EGENERIC;
805             break;
806         }
807
808         default:
809             return VLC_EGENERIC;
810
811     }
812     return VLC_SUCCESS;
813 }
814
815 static int WriteHeaders( access_t *access, const char *fmt, ... )
816 {
817     access_sys_t *sys = access->p_sys;
818     char *str;
819     va_list args;
820     int len;
821
822     va_start( args, fmt );
823     len = vasprintf( &str, fmt, args );
824     if( likely(len >= 0) )
825     {
826         if( ((sys->p_tls != NULL)
827             ? vlc_tls_Write( sys->p_tls, str, len )
828             : net_Write( access, sys->fd, str, len )) < len )
829             len = -1;
830         free( str );
831     }
832     va_end( args );
833     return len;
834 }
835
836 /*****************************************************************************
837  * Connect:
838  *****************************************************************************/
839 static int Connect( access_t *p_access, uint64_t i_tell )
840 {
841     access_sys_t   *p_sys = p_access->p_sys;
842     vlc_url_t      srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url;
843
844     /* Clean info */
845     free( p_sys->psz_location );
846     free( p_sys->psz_mime );
847
848     free( p_sys->psz_icy_genre );
849     free( p_sys->psz_icy_name );
850     free( p_sys->psz_icy_title );
851
852
853     p_sys->psz_location = NULL;
854     p_sys->psz_mime = NULL;
855     p_sys->b_mms = false;
856     p_sys->b_chunked = false;
857     p_sys->i_chunk = 0;
858     p_sys->i_icy_meta = 0;
859     p_sys->i_icy_offset = i_tell;
860     p_sys->psz_icy_name = NULL;
861     p_sys->psz_icy_genre = NULL;
862     p_sys->psz_icy_title = NULL;
863     p_sys->i_remaining = 0;
864     p_sys->b_has_size = false;
865     p_sys->offset = i_tell;
866     p_sys->size = 0;
867     p_access->info.b_eof  = false;
868
869     /* Open connection */
870     assert( p_sys->fd == -1 ); /* No open sockets (leaking fds is BAD) */
871     p_sys->fd = net_ConnectTCP( p_access, srv.psz_host, srv.i_port );
872     if( p_sys->fd == -1 )
873     {
874         msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port );
875         return -1;
876     }
877     setsockopt (p_sys->fd, SOL_SOCKET, SO_KEEPALIVE, &(int){ 1 }, sizeof (int));
878
879     /* Initialize TLS/SSL session */
880     if( p_sys->p_creds != NULL )
881     {
882         /* CONNECT to establish TLS tunnel through HTTP proxy */
883         if( p_sys->b_proxy )
884         {
885             char *psz;
886             unsigned i_status;
887
888             WriteHeaders( p_access,
889                           "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\n\r\n",
890                           p_sys->url.psz_host, p_sys->url.i_port,
891                           p_sys->url.psz_host, p_sys->url.i_port);
892
893             psz = net_Gets( p_access, p_sys->fd );
894             if( psz == NULL )
895             {
896                 msg_Err( p_access, "cannot establish HTTP/TLS tunnel" );
897                 Disconnect( p_access );
898                 return -1;
899             }
900
901             if( sscanf( psz, "HTTP/1.%*u %3u", &i_status ) != 1 )
902                 i_status = 0;
903             free( psz );
904
905             if( ( i_status / 100 ) != 2 )
906             {
907                 msg_Err( p_access, "HTTP/TLS tunnel through proxy denied" );
908                 Disconnect( p_access );
909                 return -1;
910             }
911
912             do
913             {
914                 psz = net_Gets( p_access, p_sys->fd );
915                 if( psz == NULL )
916                 {
917                     msg_Err( p_access, "HTTP proxy connection failed" );
918                     Disconnect( p_access );
919                     return -1;
920                 }
921
922                 if( *psz == '\0' )
923                     i_status = 0;
924
925                 free( psz );
926             }
927             while( i_status );
928         }
929
930         /* TLS/SSL handshake */
931         const char *alpn[] = { "http/1.1", NULL };
932
933         p_sys->p_tls = vlc_tls_ClientSessionCreateFD( p_sys->p_creds, p_sys->fd,
934                                                       p_sys->url.psz_host,
935                                                       "https", alpn, NULL );
936         if( p_sys->p_tls == NULL )
937         {
938             msg_Err( p_access, "cannot establish HTTP/TLS session" );
939             Disconnect( p_access );
940             return -1;
941         }
942     }
943
944     return Request( p_access, i_tell ) ? -2 : 0;
945 }
946
947
948 static int Request( access_t *p_access, uint64_t i_tell )
949 {
950     access_sys_t   *p_sys = p_access->p_sys;
951     char           *psz ;
952
953     p_sys->i_remaining = 0;
954
955     const char *psz_path = p_sys->url.psz_path;
956     if( !psz_path || !*psz_path )
957         psz_path = "/";
958     if( p_sys->b_proxy && p_sys->p_tls == NULL )
959         WriteHeaders( p_access, "GET http://%s:%d%s%s%s HTTP/1.1\r\n",
960                       p_sys->url.psz_host, p_sys->url.i_port,
961                       psz_path, p_sys->url.psz_option ? "?" : "",
962                       p_sys->url.psz_option ? p_sys->url.psz_option : "" );
963     else
964         WriteHeaders( p_access, "GET %s%s%s HTTP/1.1\r\n",
965                       psz_path, p_sys->url.psz_option ? "?" : "",
966                       p_sys->url.psz_option ? p_sys->url.psz_option : "" );
967     if( p_sys->url.i_port != (p_sys->p_tls ? 443 : 80) )
968         WriteHeaders( p_access, "Host: %s:%d\r\n",
969                       p_sys->url.psz_host, p_sys->url.i_port );
970     else
971         WriteHeaders( p_access, "Host: %s\r\n", p_sys->url.psz_host );
972     /* User Agent */
973     WriteHeaders( p_access, "User-Agent: %s\r\n", p_sys->psz_user_agent );
974     /* Referrer */
975     if (p_sys->psz_referrer)
976         WriteHeaders( p_access, "Referer: %s\r\n", p_sys->psz_referrer );
977     /* Offset */
978     if( !p_sys->b_continuous )
979         WriteHeaders( p_access, "Range: bytes=%"PRIu64"-\r\n", i_tell );
980     WriteHeaders( p_access, "Connection: close\r\n" );
981
982     /* Cookies */
983     if( p_sys->cookies )
984     {
985         char * psz_cookiestring = vlc_http_cookies_for_url( p_sys->cookies, &p_sys->url );
986         if ( psz_cookiestring )
987         {
988             msg_Dbg( p_access, "Sending Cookie %s", psz_cookiestring );
989             WriteHeaders( p_access, "Cookie: %s\r\n", psz_cookiestring );
990             free( psz_cookiestring );
991         }
992     }
993
994     /* Authentication */
995     if( p_sys->url.psz_username && p_sys->url.psz_password )
996         AuthReply( p_access, "", &p_sys->url, &p_sys->auth );
997
998     /* Proxy Authentication */
999     if( p_sys->b_proxy && p_sys->proxy.psz_username != NULL
1000      && p_sys->proxy.psz_password != NULL )
1001         AuthReply( p_access, "Proxy-", &p_sys->proxy, &p_sys->proxy_auth );
1002
1003     /* ICY meta data request */
1004     WriteHeaders( p_access, "Icy-MetaData: 1\r\n" );
1005
1006     if( WriteHeaders( p_access, "\r\n" ) < 0 )
1007     {
1008         msg_Err( p_access, "failed to send request" );
1009         Disconnect( p_access );
1010         return VLC_EGENERIC;
1011     }
1012
1013     /* Read Answer */
1014     if( p_sys->p_tls != NULL )
1015         psz = vlc_tls_GetLine( p_sys->p_tls );
1016     else
1017         psz = net_Gets( p_access, p_sys->fd );
1018     if( psz == NULL )
1019     {
1020         msg_Err( p_access, "failed to read answer" );
1021         goto error;
1022     }
1023     if( !strncmp( psz, "HTTP/1.", 7 ) )
1024     {
1025         p_sys->i_code = atoi( &psz[9] );
1026         msg_Dbg( p_access, "HTTP answer code %d", p_sys->i_code );
1027     }
1028     else if( !strncmp( psz, "ICY", 3 ) )
1029     {
1030         p_sys->i_code = atoi( &psz[4] );
1031         msg_Dbg( p_access, "ICY answer code %d", p_sys->i_code );
1032         p_sys->b_icecast = true;
1033         p_sys->b_reconnect = true;
1034         p_sys->b_seekable = false;
1035     }
1036     else
1037     {
1038         msg_Err( p_access, "invalid HTTP reply '%s'", psz );
1039         free( psz );
1040         goto error;
1041     }
1042     if( p_sys->i_code != 206 && p_sys->i_code != 401 )
1043     {
1044         p_sys->b_seekable = false;
1045     }
1046     /* Authentication error - We'll have to display the dialog */
1047     if( p_sys->i_code == 401 )
1048     {
1049
1050     }
1051     /* Other fatal error */
1052     else if( p_sys->i_code >= 400 )
1053     {
1054         msg_Err( p_access, "error: %s", psz );
1055         free( psz );
1056         goto error;
1057     }
1058     free( psz );
1059
1060     for( ;; )
1061     {
1062         char *psz, *p, *p_trailing;
1063
1064         if( p_sys->p_tls != NULL )
1065             psz = vlc_tls_GetLine( p_sys->p_tls );
1066         else
1067             psz = net_Gets( p_access, p_sys->fd );
1068         if( psz == NULL )
1069         {
1070             msg_Err( p_access, "failed to read answer" );
1071             goto error;
1072         }
1073
1074         /* msg_Dbg( p_input, "Line=%s", psz ); */
1075         if( *psz == '\0' )
1076         {
1077             free( psz );
1078             break;
1079         }
1080
1081         if( ( p = strchr( psz, ':' ) ) == NULL )
1082         {
1083             msg_Err( p_access, "malformed header line: %s", psz );
1084             free( psz );
1085             goto error;
1086         }
1087         *p++ = '\0';
1088         p += strspn( p, " \t" );
1089
1090         /* trim trailing white space */
1091         p_trailing = p + strlen( p );
1092         if( p_trailing > p )
1093         {
1094             p_trailing--;
1095             while( ( *p_trailing == ' ' || *p_trailing == '\t' ) && p_trailing > p )
1096             {
1097                 *p_trailing = '\0';
1098                 p_trailing--;
1099             }
1100         }
1101
1102         if( !strcasecmp( psz, "Content-Length" ) )
1103         {
1104             uint64_t i_size = i_tell + (p_sys->i_remaining = (uint64_t)atoll( p ));
1105             if(i_size > p_sys->size) {
1106                 p_sys->b_has_size = true;
1107                 p_sys->size = i_size;
1108             }
1109             msg_Dbg( p_access, "this frame size=%"PRIu64, p_sys->i_remaining );
1110         }
1111         else if( !strcasecmp( psz, "Content-Range" ) ) {
1112             uint64_t i_ntell = i_tell;
1113             uint64_t i_nend = (p_sys->size > 0) ? (p_sys->size - 1) : i_tell;
1114             uint64_t i_nsize = p_sys->size;
1115             sscanf(p,"bytes %"SCNu64"-%"SCNu64"/%"SCNu64,&i_ntell,&i_nend,&i_nsize);
1116             if(i_nend > i_ntell ) {
1117                 p_sys->offset = i_ntell;
1118                 p_sys->i_icy_offset  = i_ntell;
1119                 p_sys->i_remaining = i_nend+1-i_ntell;
1120                 uint64_t i_size = (i_nsize > i_nend) ? i_nsize : (i_nend + 1);
1121                 if(i_size > p_sys->size) {
1122                     p_sys->b_has_size = true;
1123                     p_sys->size = i_size;
1124                 }
1125                 msg_Dbg( p_access, "stream size=%"PRIu64",pos=%"PRIu64",remaining=%"PRIu64,
1126                          i_nsize, i_ntell, p_sys->i_remaining);
1127             }
1128         }
1129         else if( !strcasecmp( psz, "Location" ) )
1130         {
1131             char * psz_new_loc;
1132
1133             /* This does not follow RFC 2068, but yet if the url is not absolute,
1134              * handle it as everyone does. */
1135             if( p[0] == '/' )
1136             {
1137                 const char *psz_http_ext = p_sys->p_tls ? "s" : "" ;
1138
1139                 if( p_sys->url.i_port == ( p_sys->p_tls ? 443 : 80 ) )
1140                 {
1141                     if( asprintf(&psz_new_loc, "http%s://%s%s", psz_http_ext,
1142                                  p_sys->url.psz_host, p) < 0 )
1143                         goto error;
1144                 }
1145                 else
1146                 {
1147                     if( asprintf(&psz_new_loc, "http%s://%s:%d%s", psz_http_ext,
1148                                  p_sys->url.psz_host, p_sys->url.i_port, p) < 0 )
1149                         goto error;
1150                 }
1151             }
1152             else
1153             {
1154                 psz_new_loc = strdup( p );
1155             }
1156
1157             free( p_sys->psz_location );
1158             p_sys->psz_location = psz_new_loc;
1159         }
1160         else if( !strcasecmp( psz, "Content-Type" ) )
1161         {
1162             free( p_sys->psz_mime );
1163             p_sys->psz_mime = strdup( p );
1164             msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime );
1165         }
1166         else if( !strcasecmp( psz, "Content-Encoding" ) )
1167         {
1168             msg_Dbg( p_access, "Content-Encoding: %s", p );
1169         }
1170         else if( !strcasecmp( psz, "Pragma" ) )
1171         {
1172             if( !strcasecmp( psz, "Pragma: features" ) )
1173                 p_sys->b_mms = true;
1174             msg_Dbg( p_access, "Pragma: %s", p );
1175         }
1176         else if( !strcasecmp( psz, "Server" ) )
1177         {
1178             msg_Dbg( p_access, "Server: %s", p );
1179             if( !strncasecmp( p, "Icecast", 7 ) ||
1180                 !strncasecmp( p, "Nanocaster", 10 ) )
1181             {
1182                 /* Remember if this is Icecast
1183                  * we need to force demux in this case without breaking
1184                  *  autodetection */
1185
1186                 /* Let live 365 streams (nanocaster) piggyback on the icecast
1187                  * routine. They look very similar */
1188
1189                 p_sys->b_reconnect = true;
1190                 p_sys->b_pace_control = false;
1191                 p_sys->b_icecast = true;
1192             }
1193         }
1194         else if( !strcasecmp( psz, "Transfer-Encoding" ) )
1195         {
1196             msg_Dbg( p_access, "Transfer-Encoding: %s", p );
1197             if( !strncasecmp( p, "chunked", 7 ) )
1198             {
1199                 p_sys->b_chunked = true;
1200             }
1201         }
1202         else if( !strcasecmp( psz, "Icy-MetaInt" ) )
1203         {
1204             msg_Dbg( p_access, "Icy-MetaInt: %s", p );
1205             p_sys->i_icy_meta = atoi( p );
1206             if( p_sys->i_icy_meta < 0 )
1207                 p_sys->i_icy_meta = 0;
1208             if( p_sys->i_icy_meta > 0 )
1209                 p_sys->b_icecast = true;
1210
1211             msg_Warn( p_access, "ICY metaint=%d", p_sys->i_icy_meta );
1212         }
1213         else if( !strcasecmp( psz, "Icy-Name" ) )
1214         {
1215             free( p_sys->psz_icy_name );
1216             char *psz_tmp = strdup( p );
1217             p_sys->psz_icy_name = EnsureUTF8( psz_tmp );
1218             if( !p_sys->psz_icy_name )
1219                 free( psz_tmp );
1220             else
1221                 vlc_xml_decode( p_sys->psz_icy_name );
1222             msg_Dbg( p_access, "Icy-Name: %s", p_sys->psz_icy_name );
1223             input_thread_t *p_input = p_access->p_input;
1224             if ( p_input )
1225             {
1226                 input_item_t *p_input_item = input_GetItem( p_access->p_input );
1227                 if ( p_input_item )
1228                     input_item_SetMeta( p_input_item, vlc_meta_Title, p_sys->psz_icy_name );
1229             }
1230
1231             p_sys->b_icecast = true; /* be on the safeside. set it here as well. */
1232             p_sys->b_reconnect = true;
1233             p_sys->b_pace_control = false;
1234         }
1235         else if( !strcasecmp( psz, "Icy-Genre" ) )
1236         {
1237             free( p_sys->psz_icy_genre );
1238             char *psz_tmp = strdup( p );
1239             p_sys->psz_icy_genre = EnsureUTF8( psz_tmp );
1240             if( !p_sys->psz_icy_genre )
1241                 free( psz_tmp );
1242             else
1243                 vlc_xml_decode( p_sys->psz_icy_genre );
1244             msg_Dbg( p_access, "Icy-Genre: %s", p_sys->psz_icy_genre );
1245             input_thread_t *p_input = p_access->p_input;
1246             if( p_input )
1247             {
1248                 input_item_t *p_input_item = input_GetItem( p_access->p_input );
1249                 if( p_input_item )
1250                     input_item_SetMeta( p_input_item, vlc_meta_Genre, p_sys->psz_icy_genre );
1251             }
1252         }
1253         else if( !strncasecmp( psz, "Icy-Notice", 10 ) )
1254         {
1255             msg_Dbg( p_access, "Icy-Notice: %s", p );
1256         }
1257         else if( !strncasecmp( psz, "icy-", 4 ) ||
1258                  !strncasecmp( psz, "ice-", 4 ) ||
1259                  !strncasecmp( psz, "x-audiocast", 11 ) )
1260         {
1261             msg_Dbg( p_access, "Meta-Info: %s: %s", psz, p );
1262         }
1263         else if( !strcasecmp( psz, "Set-Cookie" ) )
1264         {
1265             if( p_sys->cookies )
1266             {
1267                 if ( vlc_http_cookies_append( p_sys->cookies, p, &p_sys->url ) )
1268                     msg_Dbg( p_access, "Accepting Cookie: %s", p );
1269                 else
1270                     msg_Dbg( p_access, "Rejected Cookie: %s", p );
1271             }
1272             else
1273                 msg_Dbg( p_access, "We have a Cookie we won't remember: %s", p );
1274         }
1275         else if( !strcasecmp( psz, "www-authenticate" ) )
1276         {
1277             msg_Dbg( p_access, "Authentication header: %s", p );
1278             vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access),
1279                                                       &p_sys->auth, p );
1280         }
1281         else if( !strcasecmp( psz, "proxy-authenticate" ) )
1282         {
1283             msg_Dbg( p_access, "Proxy authentication header: %s", p );
1284             vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access),
1285                                                       &p_sys->proxy_auth, p );
1286         }
1287         else if( !strcasecmp( psz, "authentication-info" ) )
1288         {
1289             msg_Dbg( p_access, "Authentication Info header: %s", p );
1290             if( AuthCheckReply( p_access, p, &p_sys->url, &p_sys->auth ) )
1291                 goto error;
1292         }
1293         else if( !strcasecmp( psz, "proxy-authentication-info" ) )
1294         {
1295             msg_Dbg( p_access, "Proxy Authentication Info header: %s", p );
1296             if( AuthCheckReply( p_access, p, &p_sys->proxy, &p_sys->proxy_auth ) )
1297                 goto error;
1298         }
1299         else if( !strcasecmp( psz, "Accept-Ranges" ) )
1300         {
1301             if( !strcasecmp( p, "bytes" ) )
1302                 p_sys->b_seekable = true;
1303         }
1304
1305         free( psz );
1306     }
1307     return VLC_SUCCESS;
1308
1309 error:
1310     Disconnect( p_access );
1311     return VLC_EGENERIC;
1312 }
1313
1314 /*****************************************************************************
1315  * Disconnect:
1316  *****************************************************************************/
1317 static void Disconnect( access_t *p_access )
1318 {
1319     access_sys_t *p_sys = p_access->p_sys;
1320
1321     if( p_sys->p_tls != NULL)
1322         vlc_tls_Close( p_sys->p_tls );
1323     else if( p_sys->fd != -1)
1324         net_Close(p_sys->fd);
1325     p_sys->p_tls = NULL;
1326     p_sys->fd = -1;
1327 }
1328
1329 /*****************************************************************************
1330  * HTTP authentication
1331  *****************************************************************************/
1332
1333 static void AuthReply( access_t *p_access, const char *psz_prefix,
1334                        vlc_url_t *p_url, vlc_http_auth_t *p_auth )
1335 {
1336     char *psz_value;
1337
1338     psz_value =
1339         vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access), p_auth,
1340                                                  "GET", p_url->psz_path,
1341                                                  p_url->psz_username,
1342                                                  p_url->psz_password );
1343     if ( psz_value == NULL )
1344         return;
1345
1346     WriteHeaders( p_access, "%sAuthorization: %s\r\n", psz_prefix, psz_value );
1347     free( psz_value );
1348 }
1349
1350 static int AuthCheckReply( access_t *p_access, const char *psz_header,
1351                            vlc_url_t *p_url, vlc_http_auth_t *p_auth )
1352 {
1353     return
1354         vlc_http_auth_ParseAuthenticationInfoHeader( VLC_OBJECT(p_access),
1355                                                      p_auth,
1356                                                      psz_header, "",
1357                                                      p_url->psz_path,
1358                                                      p_url->psz_username,
1359                                                      p_url->psz_password );
1360 }
1361
1362 /*****************************************************************************
1363  * HTTP cookies
1364  *****************************************************************************/
1365
1366 /**
1367  * Inherit the cookie jar from the playlist
1368  *
1369  * @param p_this: http access object
1370  * @return A borrowed reference to a vlc_http_cookie_jar_t, do not free
1371  */
1372 static vlc_http_cookie_jar_t *GetCookieJar( vlc_object_t *p_this )
1373 {
1374     vlc_value_t val;
1375
1376     if ( var_Inherit( p_this, "http-cookies", VLC_VAR_ADDRESS, &val ) == VLC_SUCCESS )
1377         return val.p_address;
1378     else
1379         return NULL;
1380 }