1 /*****************************************************************************
2 * http.c: HTTP input module
3 *****************************************************************************
4 * Copyright (C) 2001-2008 VLC authors and VideoLAN
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>
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.
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.
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 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38 #include <vlc_access.h>
40 #include <vlc_network.h>
42 #include <vlc_strings.h>
43 #include <vlc_charset.h>
44 #include <vlc_input.h>
46 #include <vlc_interrupt.h>
47 #include <vlc_keystore.h>
48 #include <vlc_memstream.h>
53 /*****************************************************************************
55 *****************************************************************************/
56 static int Open ( vlc_object_t
* );
57 static void Close( vlc_object_t
* );
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." )
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." )
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 " \
75 set_description( N_("HTTP input") )
76 set_capability( "access", 0 )
77 set_shortname( N_( "HTTP(S)" ) )
78 set_category( CAT_INPUT
)
79 set_subcategory( SUBCAT_INPUT_ACCESS
)
81 add_string( "http-proxy", NULL
, PROXY_TEXT
, PROXY_LONGTEXT
,
83 add_password( "http-proxy-pwd", NULL
,
84 PROXY_PASS_TEXT
, PROXY_PASS_LONGTEXT
, false )
85 add_obsolete_bool( "http-use-IE-proxy" )
86 add_bool( "http-reconnect", false, RECONNECT_TEXT
,
87 RECONNECT_LONGTEXT
, true )
88 /* 'itpc' = iTunes Podcast */
89 add_shortcut( "http", "unsv", "itpc", "icyx" )
90 set_callbacks( Open
, Close
)
93 /*****************************************************************************
95 *****************************************************************************/
103 char *psz_user_agent
;
107 vlc_http_auth_t auth
;
112 vlc_http_auth_t proxy_auth
;
113 char *psz_proxy_passbuf
;
123 uint64_t i_icy_offset
;
137 static ssize_t
Read( access_t
*, void *, size_t );
138 static int Seek( access_t
*, uint64_t );
139 static int Control( access_t
*, int, va_list );
142 static int Connect( access_t
* );
143 static void Disconnect( access_t
* );
146 static int AuthCheckReply( access_t
*p_access
, const char *psz_header
,
147 vlc_url_t
*p_url
, vlc_http_auth_t
*p_auth
);
149 /*****************************************************************************
151 *****************************************************************************/
152 static int Open( vlc_object_t
*p_this
)
154 access_t
*p_access
= (access_t
*)p_this
;
155 const char *psz_url
= p_access
->psz_url
;
157 int ret
= VLC_EGENERIC
;
158 vlc_credential credential
;
160 access_sys_t
*p_sys
= vlc_malloc( p_this
, sizeof(*p_sys
) );
161 if( unlikely(p_sys
== NULL
) )
165 p_sys
->b_proxy
= false;
166 p_sys
->psz_proxy_passbuf
= NULL
;
167 p_sys
->psz_mime
= NULL
;
168 p_sys
->b_icecast
= false;
169 p_sys
->psz_location
= NULL
;
170 p_sys
->psz_user_agent
= NULL
;
171 p_sys
->psz_referrer
= NULL
;
172 p_sys
->psz_username
= NULL
;
173 p_sys
->psz_password
= NULL
;
174 p_sys
->i_icy_meta
= 0;
175 p_sys
->i_icy_offset
= 0;
176 p_sys
->psz_icy_name
= NULL
;
177 p_sys
->psz_icy_genre
= NULL
;
178 p_sys
->psz_icy_title
= NULL
;
179 p_sys
->b_has_size
= false;
182 p_access
->p_sys
= p_sys
;
184 if( vlc_UrlParse( &p_sys
->url
, psz_url
) || p_sys
->url
.psz_host
== NULL
)
186 msg_Err( p_access
, "invalid URL" );
187 vlc_UrlClean( &p_sys
->url
);
190 if( p_sys
->url
.i_port
<= 0 )
191 p_sys
->url
.i_port
= 80;
193 vlc_http_auth_Init( &p_sys
->auth
);
194 vlc_http_auth_Init( &p_sys
->proxy_auth
);
195 vlc_credential_init( &credential
, &p_sys
->url
);
197 /* Determine the HTTP user agent */
198 /* See RFC2616 §2.2 token and comment definition, and §3.8 and
199 * §14.43 user-agent header */
200 p_sys
->psz_user_agent
= var_InheritString( p_access
, "http-user-agent" );
201 if (p_sys
->psz_user_agent
)
203 unsigned comment_level
= 0;
204 for( char *p
= p_sys
->psz_user_agent
; *p
; p
++ )
207 if (comment_level
== 0)
209 if( c
< 32 || strchr( ")<>@,;:\\\"[]?={}", c
) )
210 *p
= '_'; /* remove potentially harmful characters */
216 else if( c
< 32 && strchr( "\t\r\n", c
) == NULL
)
217 *p
= '_'; /* remove potentially harmful characters */
221 if (comment_level
== UINT_MAX
)
226 /* truncate evil unclosed comments */
227 if (comment_level
> 0)
229 char *p
= strchr(p_sys
->psz_user_agent
, '(');
235 p_sys
->psz_referrer
= var_InheritString( p_access
, "http-referrer" );
238 psz
= var_InheritString( p_access
, "http-proxy" );
241 msg_Dbg(p_access
, "querying proxy for %s", psz_url
);
242 psz
= vlc_getProxyUrl(psz_url
);
245 msg_Dbg(p_access
, "proxy: %s", psz
);
247 msg_Dbg(p_access
, "no proxy");
251 p_sys
->b_proxy
= true;
252 vlc_UrlParse( &p_sys
->proxy
, psz
);
255 psz
= var_InheritString( p_access
, "http-proxy-pwd" );
257 p_sys
->proxy
.psz_password
= p_sys
->psz_proxy_passbuf
= psz
;
259 if( p_sys
->proxy
.psz_host
== NULL
|| *p_sys
->proxy
.psz_host
== '\0' )
261 msg_Warn( p_access
, "invalid proxy host" );
264 if( p_sys
->proxy
.i_port
<= 0 )
266 p_sys
->proxy
.i_port
= 80;
270 msg_Dbg( p_access
, "http: server='%s' port=%d file='%s'",
271 p_sys
->url
.psz_host
, p_sys
->url
.i_port
,
272 p_sys
->url
.psz_path
!= NULL
? p_sys
->url
.psz_path
: "" );
275 msg_Dbg( p_access
, " proxy %s:%d", p_sys
->proxy
.psz_host
,
276 p_sys
->proxy
.i_port
);
278 if( p_sys
->url
.psz_username
&& *p_sys
->url
.psz_username
)
280 msg_Dbg( p_access
, " user='%s'", p_sys
->url
.psz_username
);
283 p_sys
->b_reconnect
= var_InheritBool( p_access
, "http-reconnect" );
284 p_sys
->b_continuous
= var_InheritBool( p_access
, "http-continuous" );
286 if( vlc_credential_get( &credential
, p_access
, NULL
, NULL
, NULL
, NULL
) )
288 p_sys
->url
.psz_username
= (char *) credential
.psz_username
;
289 p_sys
->url
.psz_password
= (char *) credential
.psz_password
;
294 if( Connect( p_access
) )
297 if( p_sys
->i_code
== 401 )
299 if( p_sys
->auth
.psz_realm
== NULL
)
301 msg_Err( p_access
, "authentication failed without realm" );
305 if( p_sys
->url
.psz_username
&& p_sys
->url
.psz_password
&&
306 p_sys
->auth
.psz_nonce
&& p_sys
->auth
.i_nonce
== 0 )
308 Disconnect( p_access
);
311 free( p_sys
->psz_username
);
312 free( p_sys
->psz_password
);
313 p_sys
->psz_username
= p_sys
->psz_password
= NULL
;
315 msg_Dbg( p_access
, "authentication failed for realm %s",
316 p_sys
->auth
.psz_realm
);
318 credential
.psz_realm
= p_sys
->auth
.psz_realm
;
319 credential
.psz_authtype
= p_sys
->auth
.psz_nonce
? "Digest" : "Basic";
321 if( vlc_credential_get( &credential
, p_access
, NULL
, NULL
,
322 _("HTTP authentication"),
323 _("Please enter a valid login name and a "
324 "password for realm %s."), p_sys
->auth
.psz_realm
) )
326 p_sys
->psz_username
= strdup(credential
.psz_username
);
327 p_sys
->psz_password
= strdup(credential
.psz_password
);
328 if (!p_sys
->psz_username
|| !p_sys
->psz_password
)
330 msg_Err( p_access
, "retrying with user=%s", p_sys
->psz_username
);
331 p_sys
->url
.psz_username
= p_sys
->psz_username
;
332 p_sys
->url
.psz_password
= p_sys
->psz_password
;
333 Disconnect( p_access
);
340 vlc_credential_store( &credential
, p_access
);
342 if( ( p_sys
->i_code
== 301 || p_sys
->i_code
== 302 ||
343 p_sys
->i_code
== 303 || p_sys
->i_code
== 307 ) &&
344 p_sys
->psz_location
!= NULL
)
346 p_access
->psz_url
= p_sys
->psz_location
;
347 p_sys
->psz_location
= NULL
;
348 ret
= VLC_ACCESS_REDIRECT
;
352 if( p_sys
->b_reconnect
) msg_Dbg( p_access
, "auto re-connect enabled" );
354 /* Set up p_access */
355 p_access
->pf_read
= Read
;
356 p_access
->pf_control
= Control
;
357 p_access
->pf_seek
= Seek
;
359 vlc_credential_clean( &credential
);
364 vlc_credential_clean( &credential
);
365 vlc_UrlClean( &p_sys
->url
);
367 vlc_UrlClean( &p_sys
->proxy
);
368 free( p_sys
->psz_proxy_passbuf
);
369 free( p_sys
->psz_mime
);
370 free( p_sys
->psz_location
);
371 free( p_sys
->psz_user_agent
);
372 free( p_sys
->psz_referrer
);
373 free( p_sys
->psz_username
);
374 free( p_sys
->psz_password
);
376 Disconnect( p_access
);
381 /*****************************************************************************
383 *****************************************************************************/
384 static void Close( vlc_object_t
*p_this
)
386 access_t
*p_access
= (access_t
*)p_this
;
387 access_sys_t
*p_sys
= p_access
->p_sys
;
389 vlc_UrlClean( &p_sys
->url
);
390 vlc_http_auth_Deinit( &p_sys
->auth
);
392 vlc_UrlClean( &p_sys
->proxy
);
393 vlc_http_auth_Deinit( &p_sys
->proxy_auth
);
395 free( p_sys
->psz_mime
);
396 free( p_sys
->psz_location
);
398 free( p_sys
->psz_icy_name
);
399 free( p_sys
->psz_icy_genre
);
400 free( p_sys
->psz_icy_title
);
402 free( p_sys
->psz_user_agent
);
403 free( p_sys
->psz_referrer
);
404 free( p_sys
->psz_username
);
405 free( p_sys
->psz_password
);
407 Disconnect( p_access
);
410 /* Read data from the socket */
411 static int ReadData( access_t
*p_access
, int *pi_read
,
412 void *p_buffer
, size_t i_len
)
414 access_sys_t
*p_sys
= p_access
->p_sys
;
416 *pi_read
= vlc_recv_i11e( p_sys
->fd
, p_buffer
, i_len
, 0 );
417 if( *pi_read
< 0 && errno
!= EINTR
&& errno
!= EAGAIN
)
422 /*****************************************************************************
423 * Read: Read up to i_len bytes from the http connection and place in
424 * p_buffer. Return the actual number of bytes read
425 *****************************************************************************/
426 static int ReadICYMeta( access_t
*p_access
);
427 static ssize_t
Read( access_t
*p_access
, void *p_buffer
, size_t i_len
)
429 access_sys_t
*p_sys
= p_access
->p_sys
;
432 if( p_sys
->fd
== -1 )
438 if( p_sys
->i_icy_meta
> 0 && p_sys
->offset
- p_sys
->i_icy_offset
> 0 )
440 int64_t i_next
= p_sys
->i_icy_meta
-
441 (p_sys
->offset
- p_sys
->i_icy_offset
) % p_sys
->i_icy_meta
;
443 if( i_next
== p_sys
->i_icy_meta
)
445 if( ReadICYMeta( p_access
) )
452 if( ReadData( p_access
, &i_read
, p_buffer
, i_len
) )
456 return -1; /* EINTR / EAGAIN */
460 Disconnect( p_access
);
461 if( p_sys
->b_reconnect
)
463 msg_Dbg( p_access
, "got disconnected, trying to reconnect" );
464 if( Connect( p_access
) )
465 msg_Dbg( p_access
, "reconnection failed" );
472 assert( i_read
>= 0 );
473 p_sys
->offset
+= i_read
;
478 static int ReadICYMeta( access_t
*p_access
)
480 access_sys_t
*p_sys
= p_access
->p_sys
;
486 /* Read meta data length */
487 if( ReadData( p_access
, &i_read
, &buffer
, 1 ) )
491 const int i_size
= buffer
<< 4;
492 /* msg_Dbg( p_access, "ICY meta size=%u", i_size); */
494 psz_meta
= malloc( i_size
+ 1 );
495 for( i_read
= 0; i_read
< i_size
; )
498 if( ReadData( p_access
, &i_tmp
, (uint8_t *)&psz_meta
[i_read
], i_size
- i_read
) || i_tmp
<= 0 )
505 psz_meta
[i_read
] = '\0'; /* Just in case */
507 /* msg_Dbg( p_access, "icy-meta=%s", psz_meta ); */
509 /* Now parse the meta */
510 /* Look for StreamTitle= */
511 p
= strcasestr( (char *)psz_meta
, "StreamTitle=" );
514 p
+= strlen( "StreamTitle=" );
515 if( *p
== '\'' || *p
== '"' )
517 char closing
[] = { p
[0], ';', '\0' };
518 char *psz
= strstr( &p
[1], closing
);
520 psz
= strchr( &p
[1], ';' );
522 if( psz
) *psz
= '\0';
526 char *psz
= strchr( &p
[1], ';' );
527 if( psz
) *psz
= '\0';
530 if( !p_sys
->psz_icy_title
||
531 strcmp( p_sys
->psz_icy_title
, &p
[1] ) )
533 free( p_sys
->psz_icy_title
);
534 char *psz_tmp
= strdup( &p
[1] );
535 p_sys
->psz_icy_title
= EnsureUTF8( psz_tmp
);
536 if( !p_sys
->psz_icy_title
)
539 msg_Dbg( p_access
, "New Icy-Title=%s", p_sys
->psz_icy_title
);
540 input_thread_t
*p_input
= p_access
->p_input
;
543 input_item_t
*p_input_item
= input_GetItem( p_access
->p_input
);
545 input_item_SetMeta( p_input_item
, vlc_meta_NowPlaying
, p_sys
->psz_icy_title
);
554 /*****************************************************************************
555 * Seek: close and re-open a connection at the right place
556 *****************************************************************************/
557 static int Seek( access_t
*p_access
, uint64_t i_pos
)
559 (void) p_access
; (void) i_pos
;
563 /*****************************************************************************
565 *****************************************************************************/
566 static int Control( access_t
*p_access
, int i_query
, va_list args
)
568 access_sys_t
*p_sys
= p_access
->p_sys
;
575 case STREAM_CAN_SEEK
:
576 case STREAM_CAN_FASTSEEK
:
577 pb_bool
= va_arg( args
, bool* );
580 case STREAM_CAN_PAUSE
:
581 case STREAM_CAN_CONTROL_PACE
:
582 pb_bool
= va_arg( args
, bool* );
587 case STREAM_GET_PTS_DELAY
:
588 pi_64
= va_arg( args
, int64_t * );
589 *pi_64
= INT64_C(1000)
590 * var_InheritInteger( p_access
, "network-caching" );
593 case STREAM_GET_SIZE
:
594 if( !p_sys
->b_has_size
)
596 *va_arg( args
, uint64_t*) = p_sys
->size
;
600 case STREAM_SET_PAUSE_STATE
:
603 case STREAM_GET_CONTENT_TYPE
:
605 char **type
= va_arg( args
, char ** );
607 if( p_sys
->b_icecast
&& p_sys
->psz_mime
== NULL
)
608 *type
= strdup( "audio/mpeg" );
609 else if( !strcasecmp( p_access
->psz_name
, "itpc" ) )
610 *type
= strdup( "application/rss+xml" );
611 else if( !strcasecmp( p_access
->psz_name
, "unsv" ) &&
612 p_sys
->psz_mime
!= NULL
&&
613 !strcasecmp( p_sys
->psz_mime
, "misc/ultravox" ) )
614 /* Grrrr! detect ultravox server and force NSV demuxer */
615 *type
= strdup( "video/nsa" );
616 else if( p_sys
->psz_mime
)
617 *type
= strdup( p_sys
->psz_mime
);
630 /*****************************************************************************
632 *****************************************************************************/
633 static int Connect( access_t
*p_access
)
635 access_sys_t
*p_sys
= p_access
->p_sys
;
636 vlc_url_t srv
= p_sys
->b_proxy
? p_sys
->proxy
: p_sys
->url
;
640 free( p_sys
->psz_location
);
641 free( p_sys
->psz_mime
);
643 free( p_sys
->psz_icy_genre
);
644 free( p_sys
->psz_icy_name
);
645 free( p_sys
->psz_icy_title
);
648 p_sys
->psz_location
= NULL
;
649 p_sys
->psz_mime
= NULL
;
650 p_sys
->i_icy_meta
= 0;
651 p_sys
->i_icy_offset
= 0;
652 p_sys
->psz_icy_name
= NULL
;
653 p_sys
->psz_icy_genre
= NULL
;
654 p_sys
->psz_icy_title
= NULL
;
655 p_sys
->b_has_size
= false;
659 struct vlc_memstream stream
;
661 vlc_memstream_open(&stream
);
663 vlc_memstream_puts(&stream
, "GET ");
665 vlc_memstream_printf( &stream
, "http://%s:%d",
666 p_sys
->url
.psz_host
, p_sys
->url
.i_port
);
667 if( p_sys
->url
.psz_path
== NULL
|| p_sys
->url
.psz_path
[0] == '\0' )
668 vlc_memstream_putc( &stream
, '/' );
670 vlc_memstream_puts( &stream
, p_sys
->url
.psz_path
);
671 if( p_sys
->url
.psz_option
!= NULL
)
672 vlc_memstream_printf( &stream
, "?%s", p_sys
->url
.psz_option
);
673 vlc_memstream_puts( &stream
, " HTTP/1.0\r\n" );
675 vlc_memstream_printf( &stream
, "Host: %s", p_sys
->url
.psz_host
);
676 if( p_sys
->url
.i_port
!= 80 )
677 vlc_memstream_printf( &stream
, ":%d", p_sys
->url
.i_port
);
678 vlc_memstream_puts( &stream
, "\r\n" );
681 vlc_memstream_printf( &stream
, "User-Agent: %s\r\n",
682 p_sys
->psz_user_agent
);
684 if (p_sys
->psz_referrer
)
685 vlc_memstream_printf( &stream
, "Referer: %s\r\n",
686 p_sys
->psz_referrer
);
689 if( p_sys
->url
.psz_username
!= NULL
&& p_sys
->url
.psz_password
!= NULL
)
693 auth
= vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access
),
694 &p_sys
->auth
, "GET", p_sys
->url
.psz_path
,
695 p_sys
->url
.psz_username
, p_sys
->url
.psz_password
);
697 vlc_memstream_printf( &stream
, "Authorization: %s\r\n", auth
);
701 /* Proxy Authentication */
702 if( p_sys
->b_proxy
&& p_sys
->proxy
.psz_username
!= NULL
703 && p_sys
->proxy
.psz_password
!= NULL
)
707 auth
= vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access
),
708 &p_sys
->proxy_auth
, "GET", p_sys
->url
.psz_path
,
709 p_sys
->proxy
.psz_username
, p_sys
->proxy
.psz_password
);
711 vlc_memstream_printf( &stream
, "Proxy-Authorization: %s\r\n",
716 /* ICY meta data request */
717 vlc_memstream_puts( &stream
, "Icy-MetaData: 1\r\n" );
719 vlc_memstream_puts( &stream
, "\r\n" );
721 if( vlc_memstream_close( &stream
) )
724 /* Open connection */
725 assert( p_sys
->fd
== -1 ); /* No open sockets (leaking fds is BAD) */
726 p_sys
->fd
= net_ConnectTCP( p_access
, srv
.psz_host
, srv
.i_port
);
727 if( p_sys
->fd
== -1 )
729 msg_Err( p_access
, "cannot connect to %s:%d", srv
.psz_host
, srv
.i_port
);
733 setsockopt (p_sys
->fd
, SOL_SOCKET
, SO_KEEPALIVE
, &(int){ 1 }, sizeof (int));
735 msg_Dbg( p_access
, "sending request:\n%s", stream
.ptr
);
736 val
= net_Write( p_access
, p_sys
->fd
, stream
.ptr
, stream
.length
);
739 if( val
< (ssize_t
)stream
.length
)
741 msg_Err( p_access
, "failed to send request" );
742 Disconnect( p_access
);
747 char *psz
= net_Gets( p_access
, p_sys
->fd
);
750 msg_Err( p_access
, "failed to read answer" );
753 if( !strncmp( psz
, "HTTP/1.", 7 ) )
755 p_sys
->i_code
= atoi( &psz
[9] );
756 msg_Dbg( p_access
, "HTTP answer code %d", p_sys
->i_code
);
758 else if( !strncmp( psz
, "ICY", 3 ) )
760 p_sys
->i_code
= atoi( &psz
[4] );
761 msg_Dbg( p_access
, "ICY answer code %d", p_sys
->i_code
);
762 p_sys
->b_icecast
= true;
763 p_sys
->b_reconnect
= true;
767 msg_Err( p_access
, "invalid HTTP reply '%s'", psz
);
771 /* Authentication error - We'll have to display the dialog */
772 if( p_sys
->i_code
== 401 )
776 /* Other fatal error */
777 else if( p_sys
->i_code
>= 400 )
779 msg_Err( p_access
, "error: %s", psz
);
787 char *p
, *p_trailing
;
789 char *psz
= net_Gets( p_access
, p_sys
->fd
);
792 msg_Err( p_access
, "failed to read answer" );
796 /* msg_Dbg( p_input, "Line=%s", psz ); */
803 if( ( p
= strchr( psz
, ':' ) ) == NULL
)
805 msg_Err( p_access
, "malformed header line: %s", psz
);
810 p
+= strspn( p
, " \t" );
812 /* trim trailing white space */
813 p_trailing
= p
+ strlen( p
);
817 while( ( *p_trailing
== ' ' || *p_trailing
== '\t' ) && p_trailing
> p
)
824 if( !strcasecmp( psz
, "Content-Length" ) )
826 uint64_t i_size
= (uint64_t)atoll( p
);
827 if(i_size
> p_sys
->size
) {
828 p_sys
->b_has_size
= true;
829 p_sys
->size
= i_size
;
832 else if( !strcasecmp( psz
, "Location" ) )
836 /* This does not follow RFC 2068, but yet if the url is not absolute,
837 * handle it as everyone does. */
840 if( p_sys
->url
.i_port
== 80 )
842 if( asprintf(&psz_new_loc
, "http://%s%s",
843 p_sys
->url
.psz_host
, p
) < 0 )
848 if( asprintf(&psz_new_loc
, "http://%s:%d%s",
849 p_sys
->url
.psz_host
, p_sys
->url
.i_port
, p
) < 0 )
855 psz_new_loc
= strdup( p
);
858 free( p_sys
->psz_location
);
859 p_sys
->psz_location
= psz_new_loc
;
861 else if( !strcasecmp( psz
, "Content-Type" ) )
863 free( p_sys
->psz_mime
);
864 p_sys
->psz_mime
= strdup( p
);
865 msg_Dbg( p_access
, "Content-Type: %s", p_sys
->psz_mime
);
867 else if( !strcasecmp( psz
, "Content-Encoding" ) )
869 msg_Dbg( p_access
, "Content-Encoding: %s", p
);
871 else if( !strcasecmp( psz
, "Server" ) )
873 msg_Dbg( p_access
, "Server: %s", p
);
874 if( !strncasecmp( p
, "Icecast", 7 ) ||
875 !strncasecmp( p
, "Nanocaster", 10 ) )
877 /* Remember if this is Icecast
878 * we need to force demux in this case without breaking
881 /* Let live 365 streams (nanocaster) piggyback on the icecast
882 * routine. They look very similar */
884 p_sys
->b_reconnect
= true;
885 p_sys
->b_icecast
= true;
888 else if( !strcasecmp( psz
, "Icy-MetaInt" ) )
890 msg_Dbg( p_access
, "Icy-MetaInt: %s", p
);
891 p_sys
->i_icy_meta
= atoi( p
);
892 if( p_sys
->i_icy_meta
< 0 )
893 p_sys
->i_icy_meta
= 0;
894 if( p_sys
->i_icy_meta
> 0 )
895 p_sys
->b_icecast
= true;
897 msg_Warn( p_access
, "ICY metaint=%d", p_sys
->i_icy_meta
);
899 else if( !strcasecmp( psz
, "Icy-Name" ) )
901 free( p_sys
->psz_icy_name
);
902 char *psz_tmp
= strdup( p
);
903 p_sys
->psz_icy_name
= EnsureUTF8( psz_tmp
);
904 if( !p_sys
->psz_icy_name
)
907 vlc_xml_decode( p_sys
->psz_icy_name
);
908 msg_Dbg( p_access
, "Icy-Name: %s", p_sys
->psz_icy_name
);
909 input_thread_t
*p_input
= p_access
->p_input
;
912 input_item_t
*p_input_item
= input_GetItem( p_access
->p_input
);
914 input_item_SetMeta( p_input_item
, vlc_meta_Title
, p_sys
->psz_icy_name
);
917 p_sys
->b_icecast
= true; /* be on the safeside. set it here as well. */
918 p_sys
->b_reconnect
= true;
920 else if( !strcasecmp( psz
, "Icy-Genre" ) )
922 free( p_sys
->psz_icy_genre
);
923 char *psz_tmp
= strdup( p
);
924 p_sys
->psz_icy_genre
= EnsureUTF8( psz_tmp
);
925 if( !p_sys
->psz_icy_genre
)
928 vlc_xml_decode( p_sys
->psz_icy_genre
);
929 msg_Dbg( p_access
, "Icy-Genre: %s", p_sys
->psz_icy_genre
);
930 input_thread_t
*p_input
= p_access
->p_input
;
933 input_item_t
*p_input_item
= input_GetItem( p_access
->p_input
);
935 input_item_SetMeta( p_input_item
, vlc_meta_Genre
, p_sys
->psz_icy_genre
);
938 else if( !strncasecmp( psz
, "Icy-Notice", 10 ) )
940 msg_Dbg( p_access
, "Icy-Notice: %s", p
);
942 else if( !strncasecmp( psz
, "icy-", 4 ) ||
943 !strncasecmp( psz
, "ice-", 4 ) ||
944 !strncasecmp( psz
, "x-audiocast", 11 ) )
946 msg_Dbg( p_access
, "Meta-Info: %s: %s", psz
, p
);
948 else if( !strcasecmp( psz
, "www-authenticate" ) )
950 msg_Dbg( p_access
, "Authentication header: %s", p
);
951 vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access
),
954 else if( !strcasecmp( psz
, "proxy-authenticate" ) )
956 msg_Dbg( p_access
, "Proxy authentication header: %s", p
);
957 vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access
),
958 &p_sys
->proxy_auth
, p
);
960 else if( !strcasecmp( psz
, "authentication-info" ) )
962 msg_Dbg( p_access
, "Authentication Info header: %s", p
);
963 if( AuthCheckReply( p_access
, p
, &p_sys
->url
, &p_sys
->auth
) )
966 else if( !strcasecmp( psz
, "proxy-authentication-info" ) )
968 msg_Dbg( p_access
, "Proxy Authentication Info header: %s", p
);
969 if( AuthCheckReply( p_access
, p
, &p_sys
->proxy
, &p_sys
->proxy_auth
) )
978 Disconnect( p_access
);
982 /*****************************************************************************
984 *****************************************************************************/
985 static void Disconnect( access_t
*p_access
)
987 access_sys_t
*p_sys
= p_access
->p_sys
;
990 net_Close(p_sys
->fd
);
994 /*****************************************************************************
995 * HTTP authentication
996 *****************************************************************************/
998 static int AuthCheckReply( access_t
*p_access
, const char *psz_header
,
999 vlc_url_t
*p_url
, vlc_http_auth_t
*p_auth
)
1002 vlc_http_auth_ParseAuthenticationInfoHeader( VLC_OBJECT(p_access
),
1006 p_url
->psz_username
,
1007 p_url
->psz_password
);