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
;
136 static ssize_t
Read( stream_t
*, void *, size_t );
137 static int Seek( stream_t
*, uint64_t );
138 static int Control( stream_t
*, int, va_list );
141 static int Connect( stream_t
* );
142 static void Disconnect( stream_t
* );
145 static int AuthCheckReply( stream_t
*p_access
, const char *psz_header
,
146 vlc_url_t
*p_url
, vlc_http_auth_t
*p_auth
);
148 /*****************************************************************************
150 *****************************************************************************/
151 static int Open( vlc_object_t
*p_this
)
153 stream_t
*p_access
= (stream_t
*)p_this
;
154 const char *psz_url
= p_access
->psz_url
;
156 int ret
= VLC_EGENERIC
;
157 vlc_credential credential
;
159 access_sys_t
*p_sys
= vlc_obj_alloc( p_this
, 1, sizeof(*p_sys
) );
160 if( unlikely(p_sys
== NULL
) )
164 p_sys
->b_proxy
= false;
165 p_sys
->psz_proxy_passbuf
= NULL
;
166 p_sys
->psz_mime
= NULL
;
167 p_sys
->b_icecast
= false;
168 p_sys
->psz_location
= NULL
;
169 p_sys
->psz_user_agent
= NULL
;
170 p_sys
->psz_referrer
= NULL
;
171 p_sys
->psz_username
= NULL
;
172 p_sys
->psz_password
= NULL
;
173 p_sys
->i_icy_meta
= 0;
174 p_sys
->i_icy_offset
= 0;
175 p_sys
->psz_icy_name
= NULL
;
176 p_sys
->psz_icy_genre
= NULL
;
177 p_sys
->psz_icy_title
= NULL
;
178 p_sys
->b_has_size
= false;
181 p_access
->p_sys
= p_sys
;
183 if( vlc_UrlParse( &p_sys
->url
, psz_url
) || p_sys
->url
.psz_host
== NULL
)
185 msg_Err( p_access
, "invalid URL" );
186 vlc_UrlClean( &p_sys
->url
);
189 if( p_sys
->url
.i_port
<= 0 )
190 p_sys
->url
.i_port
= 80;
192 vlc_credential_init( &credential
, &p_sys
->url
);
194 /* Determine the HTTP user agent */
195 /* See RFC2616 §2.2 token and comment definition, and §3.8 and
196 * §14.43 user-agent header */
197 p_sys
->psz_user_agent
= var_InheritString( p_access
, "http-user-agent" );
198 if (p_sys
->psz_user_agent
)
200 unsigned comment_level
= 0;
201 for( char *p
= p_sys
->psz_user_agent
; *p
; p
++ )
204 if (comment_level
== 0)
206 if( c
< 32 || strchr( ")<>@,;:\\\"[]?={}", c
) )
207 *p
= '_'; /* remove potentially harmful characters */
213 else if( c
< 32 && strchr( "\t\r\n", c
) == NULL
)
214 *p
= '_'; /* remove potentially harmful characters */
218 if (comment_level
== UINT_MAX
)
223 /* truncate evil unclosed comments */
224 if (comment_level
> 0)
226 char *p
= strchr(p_sys
->psz_user_agent
, '(');
232 p_sys
->psz_referrer
= var_InheritString( p_access
, "http-referrer" );
235 psz
= var_InheritString( p_access
, "http-proxy" );
238 msg_Dbg(p_access
, "querying proxy for %s", psz_url
);
239 psz
= vlc_getProxyUrl(psz_url
);
242 msg_Dbg(p_access
, "proxy: %s", psz
);
244 msg_Dbg(p_access
, "no proxy");
248 p_sys
->b_proxy
= true;
249 vlc_UrlParse( &p_sys
->proxy
, psz
);
252 psz
= var_InheritString( p_access
, "http-proxy-pwd" );
254 p_sys
->proxy
.psz_password
= p_sys
->psz_proxy_passbuf
= psz
;
256 if( p_sys
->proxy
.psz_host
== NULL
|| *p_sys
->proxy
.psz_host
== '\0' )
258 msg_Warn( p_access
, "invalid proxy host" );
261 if( p_sys
->proxy
.i_port
<= 0 )
263 p_sys
->proxy
.i_port
= 80;
267 msg_Dbg( p_access
, "http: server='%s' port=%d file='%s'",
268 p_sys
->url
.psz_host
, p_sys
->url
.i_port
,
269 p_sys
->url
.psz_path
!= NULL
? p_sys
->url
.psz_path
: "" );
272 msg_Dbg( p_access
, " proxy %s:%d", p_sys
->proxy
.psz_host
,
273 p_sys
->proxy
.i_port
);
275 if( p_sys
->url
.psz_username
&& *p_sys
->url
.psz_username
)
277 msg_Dbg( p_access
, " user='%s'", p_sys
->url
.psz_username
);
280 p_sys
->b_reconnect
= var_InheritBool( p_access
, "http-reconnect" );
282 if( vlc_credential_get( &credential
, p_access
, NULL
, NULL
, NULL
, NULL
) )
284 p_sys
->url
.psz_username
= (char *) credential
.psz_username
;
285 p_sys
->url
.psz_password
= (char *) credential
.psz_password
;
290 if( Connect( p_access
) )
293 if( p_sys
->i_code
== 401 )
295 if( p_sys
->auth
.psz_realm
== NULL
)
297 msg_Err( p_access
, "authentication failed without realm" );
301 if( p_sys
->url
.psz_username
&& p_sys
->url
.psz_password
&&
302 p_sys
->auth
.psz_nonce
&& p_sys
->auth
.i_nonce
== 0 )
304 Disconnect( p_access
);
307 free( p_sys
->psz_username
);
308 free( p_sys
->psz_password
);
309 p_sys
->psz_username
= p_sys
->psz_password
= NULL
;
311 msg_Dbg( p_access
, "authentication failed for realm %s",
312 p_sys
->auth
.psz_realm
);
314 credential
.psz_realm
= p_sys
->auth
.psz_realm
;
315 credential
.psz_authtype
= p_sys
->auth
.psz_nonce
? "Digest" : "Basic";
317 if( vlc_credential_get( &credential
, p_access
, NULL
, NULL
,
318 _("HTTP authentication"),
319 _("Please enter a valid login name and a "
320 "password for realm %s."), p_sys
->auth
.psz_realm
) )
322 p_sys
->psz_username
= strdup(credential
.psz_username
);
323 p_sys
->psz_password
= strdup(credential
.psz_password
);
324 if (!p_sys
->psz_username
|| !p_sys
->psz_password
)
326 msg_Err( p_access
, "retrying with user=%s", p_sys
->psz_username
);
327 p_sys
->url
.psz_username
= p_sys
->psz_username
;
328 p_sys
->url
.psz_password
= p_sys
->psz_password
;
329 Disconnect( p_access
);
336 vlc_credential_store( &credential
, p_access
);
338 if( ( p_sys
->i_code
== 301 || p_sys
->i_code
== 302 ||
339 p_sys
->i_code
== 303 || p_sys
->i_code
== 307 ) &&
340 p_sys
->psz_location
!= NULL
)
342 p_access
->psz_url
= p_sys
->psz_location
;
343 p_sys
->psz_location
= NULL
;
344 ret
= VLC_ACCESS_REDIRECT
;
348 if( p_sys
->b_reconnect
) msg_Dbg( p_access
, "auto re-connect enabled" );
350 /* Set up p_access */
351 p_access
->pf_read
= Read
;
352 p_access
->pf_control
= Control
;
353 p_access
->pf_seek
= Seek
;
355 vlc_credential_clean( &credential
);
360 Disconnect( p_access
);
363 vlc_credential_clean( &credential
);
364 vlc_UrlClean( &p_sys
->url
);
366 vlc_UrlClean( &p_sys
->proxy
);
367 free( p_sys
->psz_proxy_passbuf
);
368 free( p_sys
->psz_mime
);
369 free( p_sys
->psz_location
);
370 free( p_sys
->psz_user_agent
);
371 free( p_sys
->psz_referrer
);
372 free( p_sys
->psz_username
);
373 free( p_sys
->psz_password
);
378 /*****************************************************************************
380 *****************************************************************************/
381 static void Close( vlc_object_t
*p_this
)
383 stream_t
*p_access
= (stream_t
*)p_this
;
384 access_sys_t
*p_sys
= p_access
->p_sys
;
386 vlc_UrlClean( &p_sys
->url
);
388 vlc_UrlClean( &p_sys
->proxy
);
390 free( p_sys
->psz_mime
);
391 free( p_sys
->psz_location
);
393 free( p_sys
->psz_icy_name
);
394 free( p_sys
->psz_icy_genre
);
395 free( p_sys
->psz_icy_title
);
397 free( p_sys
->psz_user_agent
);
398 free( p_sys
->psz_referrer
);
399 free( p_sys
->psz_username
);
400 free( p_sys
->psz_password
);
402 Disconnect( p_access
);
405 /* Read data from the socket */
406 static int ReadData( stream_t
*p_access
, int *pi_read
,
407 void *p_buffer
, size_t i_len
)
409 access_sys_t
*p_sys
= p_access
->p_sys
;
411 *pi_read
= vlc_recv_i11e( p_sys
->fd
, p_buffer
, i_len
, 0 );
412 if( *pi_read
< 0 && errno
!= EINTR
&& errno
!= EAGAIN
)
417 /*****************************************************************************
418 * Read: Read up to i_len bytes from the http connection and place in
419 * p_buffer. Return the actual number of bytes read
420 *****************************************************************************/
421 static int ReadICYMeta( stream_t
*p_access
);
422 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
424 access_sys_t
*p_sys
= p_access
->p_sys
;
427 if( p_sys
->fd
== -1 )
433 if( p_sys
->i_icy_meta
> 0 && p_sys
->offset
- p_sys
->i_icy_offset
> 0 )
435 int i_next
= p_sys
->i_icy_meta
-
436 (p_sys
->offset
- p_sys
->i_icy_offset
) % p_sys
->i_icy_meta
;
438 if( i_next
== p_sys
->i_icy_meta
)
440 if( ReadICYMeta( p_access
) )
443 if( i_len
> (size_t)i_next
)
447 if( ReadData( p_access
, &i_read
, p_buffer
, i_len
) )
451 return -1; /* EINTR / EAGAIN */
455 Disconnect( p_access
);
456 if( p_sys
->b_reconnect
)
458 msg_Dbg( p_access
, "got disconnected, trying to reconnect" );
459 if( Connect( p_access
) )
460 msg_Dbg( p_access
, "reconnection failed" );
467 assert( i_read
>= 0 );
468 p_sys
->offset
+= i_read
;
473 static int ReadICYMeta( stream_t
*p_access
)
475 access_sys_t
*p_sys
= p_access
->p_sys
;
481 /* Read meta data length */
482 if( ReadData( p_access
, &i_read
, &buffer
, 1 ) )
486 const int i_size
= buffer
<< 4;
487 /* msg_Dbg( p_access, "ICY meta size=%u", i_size); */
489 psz_meta
= malloc( i_size
+ 1 );
490 for( i_read
= 0; i_read
< i_size
; )
493 if( ReadData( p_access
, &i_tmp
, (uint8_t *)&psz_meta
[i_read
], i_size
- i_read
) || i_tmp
<= 0 )
500 psz_meta
[i_read
] = '\0'; /* Just in case */
502 /* msg_Dbg( p_access, "icy-meta=%s", psz_meta ); */
504 /* Now parse the meta */
505 /* Look for StreamTitle= */
506 p
= strcasestr( (char *)psz_meta
, "StreamTitle=" );
509 p
+= strlen( "StreamTitle=" );
510 if( *p
== '\'' || *p
== '"' )
512 char closing
[] = { p
[0], ';', '\0' };
513 char *psz
= strstr( &p
[1], closing
);
515 psz
= strchr( &p
[1], ';' );
517 if( psz
) *psz
= '\0';
521 char *psz
= strchr( &p
[1], ';' );
522 if( psz
) *psz
= '\0';
525 if( !p_sys
->psz_icy_title
||
526 strcmp( p_sys
->psz_icy_title
, &p
[1] ) )
528 free( p_sys
->psz_icy_title
);
529 char *psz_tmp
= strdup( &p
[1] );
530 p_sys
->psz_icy_title
= EnsureUTF8( psz_tmp
);
531 if( !p_sys
->psz_icy_title
)
534 msg_Dbg( p_access
, "New Icy-Title=%s", p_sys
->psz_icy_title
);
535 input_thread_t
*p_input
= p_access
->p_input
;
538 input_item_t
*p_input_item
= input_GetItem( p_access
->p_input
);
540 input_item_SetMeta( p_input_item
, vlc_meta_NowPlaying
, p_sys
->psz_icy_title
);
549 /*****************************************************************************
550 * Seek: close and re-open a connection at the right place
551 *****************************************************************************/
552 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
554 (void) p_access
; (void) i_pos
;
558 /*****************************************************************************
560 *****************************************************************************/
561 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
563 access_sys_t
*p_sys
= p_access
->p_sys
;
570 case STREAM_CAN_SEEK
:
571 case STREAM_CAN_FASTSEEK
:
572 pb_bool
= va_arg( args
, bool* );
575 case STREAM_CAN_PAUSE
:
576 case STREAM_CAN_CONTROL_PACE
:
577 pb_bool
= va_arg( args
, bool* );
582 case STREAM_GET_PTS_DELAY
:
583 pi_64
= va_arg( args
, int64_t * );
584 *pi_64
= INT64_C(1000)
585 * var_InheritInteger( p_access
, "network-caching" );
588 case STREAM_GET_SIZE
:
589 if( !p_sys
->b_has_size
)
591 *va_arg( args
, uint64_t*) = p_sys
->size
;
595 case STREAM_SET_PAUSE_STATE
:
598 case STREAM_GET_CONTENT_TYPE
:
600 char **type
= va_arg( args
, char ** );
602 if( p_sys
->b_icecast
&& p_sys
->psz_mime
== NULL
)
603 *type
= strdup( "audio/mpeg" );
604 else if( !strcasecmp( p_access
->psz_name
, "itpc" ) )
605 *type
= strdup( "application/rss+xml" );
606 else if( !strcasecmp( p_access
->psz_name
, "unsv" ) &&
607 p_sys
->psz_mime
!= NULL
&&
608 !strcasecmp( p_sys
->psz_mime
, "misc/ultravox" ) )
609 /* Grrrr! detect ultravox server and force NSV demuxer */
610 *type
= strdup( "video/nsa" );
611 else if( p_sys
->psz_mime
)
612 *type
= strdup( p_sys
->psz_mime
);
625 /*****************************************************************************
627 *****************************************************************************/
628 static int Connect( stream_t
*p_access
)
630 access_sys_t
*p_sys
= p_access
->p_sys
;
631 vlc_url_t srv
= p_sys
->b_proxy
? p_sys
->proxy
: p_sys
->url
;
635 free( p_sys
->psz_location
);
636 free( p_sys
->psz_mime
);
638 free( p_sys
->psz_icy_genre
);
639 free( p_sys
->psz_icy_name
);
640 free( p_sys
->psz_icy_title
);
642 vlc_http_auth_Init( &p_sys
->auth
);
643 vlc_http_auth_Init( &p_sys
->proxy_auth
);
644 p_sys
->psz_location
= NULL
;
645 p_sys
->psz_mime
= NULL
;
646 p_sys
->i_icy_meta
= 0;
647 p_sys
->i_icy_offset
= 0;
648 p_sys
->psz_icy_name
= NULL
;
649 p_sys
->psz_icy_genre
= NULL
;
650 p_sys
->psz_icy_title
= NULL
;
651 p_sys
->b_has_size
= false;
655 struct vlc_memstream stream
;
657 vlc_memstream_open(&stream
);
659 vlc_memstream_puts(&stream
, "GET ");
661 vlc_memstream_printf( &stream
, "http://%s:%d",
662 p_sys
->url
.psz_host
, p_sys
->url
.i_port
);
663 if( p_sys
->url
.psz_path
== NULL
|| p_sys
->url
.psz_path
[0] == '\0' )
664 vlc_memstream_putc( &stream
, '/' );
666 vlc_memstream_puts( &stream
, p_sys
->url
.psz_path
);
667 if( p_sys
->url
.psz_option
!= NULL
)
668 vlc_memstream_printf( &stream
, "?%s", p_sys
->url
.psz_option
);
669 vlc_memstream_puts( &stream
, " HTTP/1.0\r\n" );
671 vlc_memstream_printf( &stream
, "Host: %s", p_sys
->url
.psz_host
);
672 if( p_sys
->url
.i_port
!= 80 )
673 vlc_memstream_printf( &stream
, ":%d", p_sys
->url
.i_port
);
674 vlc_memstream_puts( &stream
, "\r\n" );
677 vlc_memstream_printf( &stream
, "User-Agent: %s\r\n",
678 p_sys
->psz_user_agent
);
680 if (p_sys
->psz_referrer
)
681 vlc_memstream_printf( &stream
, "Referer: %s\r\n",
682 p_sys
->psz_referrer
);
685 if( p_sys
->url
.psz_username
!= NULL
&& p_sys
->url
.psz_password
!= NULL
)
689 auth
= vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access
),
690 &p_sys
->auth
, "GET", p_sys
->url
.psz_path
,
691 p_sys
->url
.psz_username
, p_sys
->url
.psz_password
);
693 vlc_memstream_printf( &stream
, "Authorization: %s\r\n", auth
);
697 /* Proxy Authentication */
698 if( p_sys
->b_proxy
&& p_sys
->proxy
.psz_username
!= NULL
699 && p_sys
->proxy
.psz_password
!= NULL
)
703 auth
= vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access
),
704 &p_sys
->proxy_auth
, "GET", p_sys
->url
.psz_path
,
705 p_sys
->proxy
.psz_username
, p_sys
->proxy
.psz_password
);
707 vlc_memstream_printf( &stream
, "Proxy-Authorization: %s\r\n",
712 /* ICY meta data request */
713 vlc_memstream_puts( &stream
, "Icy-MetaData: 1\r\n" );
715 vlc_memstream_puts( &stream
, "\r\n" );
717 if( vlc_memstream_close( &stream
) )
720 /* Open connection */
721 assert( p_sys
->fd
== -1 ); /* No open sockets (leaking fds is BAD) */
722 p_sys
->fd
= net_ConnectTCP( p_access
, srv
.psz_host
, srv
.i_port
);
723 if( p_sys
->fd
== -1 )
725 msg_Err( p_access
, "cannot connect to %s:%d", srv
.psz_host
, srv
.i_port
);
729 setsockopt (p_sys
->fd
, SOL_SOCKET
, SO_KEEPALIVE
, &(int){ 1 }, sizeof (int));
731 msg_Dbg( p_access
, "sending request:\n%s", stream
.ptr
);
732 val
= net_Write( p_access
, p_sys
->fd
, stream
.ptr
, stream
.length
);
735 if( val
< (ssize_t
)stream
.length
)
737 msg_Err( p_access
, "failed to send request" );
738 Disconnect( p_access
);
743 char *psz
= net_Gets( p_access
, p_sys
->fd
);
746 msg_Err( p_access
, "failed to read answer" );
749 if( !strncmp( psz
, "HTTP/1.", 7 ) )
751 p_sys
->i_code
= atoi( &psz
[9] );
752 msg_Dbg( p_access
, "HTTP answer code %d", p_sys
->i_code
);
754 else if( !strncmp( psz
, "ICY", 3 ) )
756 p_sys
->i_code
= atoi( &psz
[4] );
757 msg_Dbg( p_access
, "ICY answer code %d", p_sys
->i_code
);
758 p_sys
->b_icecast
= true;
759 p_sys
->b_reconnect
= true;
763 msg_Err( p_access
, "invalid HTTP reply '%s'", psz
);
767 /* Authentication error - We'll have to display the dialog */
768 if( p_sys
->i_code
== 401 )
772 /* Other fatal error */
773 else if( p_sys
->i_code
>= 400 )
775 msg_Err( p_access
, "error: %s", psz
);
783 char *p
, *p_trailing
;
785 psz
= net_Gets( p_access
, p_sys
->fd
);
788 msg_Err( p_access
, "failed to read answer" );
792 /* msg_Dbg( p_input, "Line=%s", psz ); */
799 if( ( p
= strchr( psz
, ':' ) ) == NULL
)
801 msg_Err( p_access
, "malformed header line: %s", psz
);
806 p
+= strspn( p
, " \t" );
808 /* trim trailing white space */
809 p_trailing
= p
+ strlen( p
);
813 while( ( *p_trailing
== ' ' || *p_trailing
== '\t' ) && p_trailing
> p
)
820 if( !strcasecmp( psz
, "Content-Length" ) )
822 uint64_t i_size
= (uint64_t)atoll( p
);
823 if(i_size
> p_sys
->size
) {
824 p_sys
->b_has_size
= true;
825 p_sys
->size
= i_size
;
828 else if( !strcasecmp( psz
, "Location" ) )
832 /* This does not follow RFC 2068, but yet if the url is not absolute,
833 * handle it as everyone does. */
836 if( p_sys
->url
.i_port
== 80 )
838 if( asprintf(&psz_new_loc
, "http://%s%s",
839 p_sys
->url
.psz_host
, p
) < 0 )
844 if( asprintf(&psz_new_loc
, "http://%s:%d%s",
845 p_sys
->url
.psz_host
, p_sys
->url
.i_port
, p
) < 0 )
851 psz_new_loc
= strdup( p
);
854 free( p_sys
->psz_location
);
855 p_sys
->psz_location
= psz_new_loc
;
857 else if( !strcasecmp( psz
, "Content-Type" ) )
859 free( p_sys
->psz_mime
);
860 p_sys
->psz_mime
= strdup( p
);
861 msg_Dbg( p_access
, "Content-Type: %s", p_sys
->psz_mime
);
863 else if( !strcasecmp( psz
, "Content-Encoding" ) )
865 msg_Dbg( p_access
, "Content-Encoding: %s", p
);
867 else if( !strcasecmp( psz
, "Server" ) )
869 msg_Dbg( p_access
, "Server: %s", p
);
870 if( !strncasecmp( p
, "Icecast", 7 ) ||
871 !strncasecmp( p
, "Nanocaster", 10 ) )
873 /* Remember if this is Icecast
874 * we need to force demux in this case without breaking
877 /* Let live 365 streams (nanocaster) piggyback on the icecast
878 * routine. They look very similar */
880 p_sys
->b_reconnect
= true;
881 p_sys
->b_icecast
= true;
884 else if( !strcasecmp( psz
, "Icy-MetaInt" ) )
886 msg_Dbg( p_access
, "Icy-MetaInt: %s", p
);
887 p_sys
->i_icy_meta
= atoi( p
);
888 if( p_sys
->i_icy_meta
< 0 )
889 p_sys
->i_icy_meta
= 0;
890 if( p_sys
->i_icy_meta
> 0 )
891 p_sys
->b_icecast
= true;
893 msg_Warn( p_access
, "ICY metaint=%d", p_sys
->i_icy_meta
);
895 else if( !strcasecmp( psz
, "Icy-Name" ) )
897 free( p_sys
->psz_icy_name
);
898 char *psz_tmp
= strdup( p
);
899 p_sys
->psz_icy_name
= EnsureUTF8( psz_tmp
);
900 if( !p_sys
->psz_icy_name
)
903 vlc_xml_decode( p_sys
->psz_icy_name
);
904 msg_Dbg( p_access
, "Icy-Name: %s", p_sys
->psz_icy_name
);
905 input_thread_t
*p_input
= p_access
->p_input
;
908 input_item_t
*p_input_item
= input_GetItem( p_access
->p_input
);
910 input_item_SetMeta( p_input_item
, vlc_meta_Title
, p_sys
->psz_icy_name
);
913 p_sys
->b_icecast
= true; /* be on the safeside. set it here as well. */
914 p_sys
->b_reconnect
= true;
916 else if( !strcasecmp( psz
, "Icy-Genre" ) )
918 free( p_sys
->psz_icy_genre
);
919 char *psz_tmp
= strdup( p
);
920 p_sys
->psz_icy_genre
= EnsureUTF8( psz_tmp
);
921 if( !p_sys
->psz_icy_genre
)
924 vlc_xml_decode( p_sys
->psz_icy_genre
);
925 msg_Dbg( p_access
, "Icy-Genre: %s", p_sys
->psz_icy_genre
);
926 input_thread_t
*p_input
= p_access
->p_input
;
929 input_item_t
*p_input_item
= input_GetItem( p_access
->p_input
);
931 input_item_SetMeta( p_input_item
, vlc_meta_Genre
, p_sys
->psz_icy_genre
);
934 else if( !strncasecmp( psz
, "Icy-Notice", 10 ) )
936 msg_Dbg( p_access
, "Icy-Notice: %s", p
);
938 else if( !strncasecmp( psz
, "icy-", 4 ) ||
939 !strncasecmp( psz
, "ice-", 4 ) ||
940 !strncasecmp( psz
, "x-audiocast", 11 ) )
942 msg_Dbg( p_access
, "Meta-Info: %s: %s", psz
, p
);
944 else if( !strcasecmp( psz
, "www-authenticate" ) )
946 msg_Dbg( p_access
, "Authentication header: %s", p
);
947 vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access
),
950 else if( !strcasecmp( psz
, "proxy-authenticate" ) )
952 msg_Dbg( p_access
, "Proxy authentication header: %s", p
);
953 vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access
),
954 &p_sys
->proxy_auth
, p
);
956 else if( !strcasecmp( psz
, "authentication-info" ) )
958 msg_Dbg( p_access
, "Authentication Info header: %s", p
);
959 if( AuthCheckReply( p_access
, p
, &p_sys
->url
, &p_sys
->auth
) )
962 else if( !strcasecmp( psz
, "proxy-authentication-info" ) )
964 msg_Dbg( p_access
, "Proxy Authentication Info header: %s", p
);
965 if( AuthCheckReply( p_access
, p
, &p_sys
->proxy
, &p_sys
->proxy_auth
) )
974 Disconnect( p_access
);
978 /*****************************************************************************
980 *****************************************************************************/
981 static void Disconnect( stream_t
*p_access
)
983 access_sys_t
*p_sys
= p_access
->p_sys
;
986 net_Close(p_sys
->fd
);
989 vlc_http_auth_Deinit( &p_sys
->auth
);
990 vlc_http_auth_Deinit( &p_sys
->proxy_auth
);
993 /*****************************************************************************
994 * HTTP authentication
995 *****************************************************************************/
997 static int AuthCheckReply( stream_t
*p_access
, const char *psz_header
,
998 vlc_url_t
*p_url
, vlc_http_auth_t
*p_auth
)
1001 vlc_http_auth_ParseAuthenticationInfoHeader( VLC_OBJECT(p_access
),
1005 p_url
->psz_username
,
1006 p_url
->psz_password
);