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_malloc( p_this
, 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
);
423 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
425 access_sys_t
*p_sys
= p_access
->p_sys
;
426 int i_total_read
= 0;
427 int i_remain_toread
= i_len
;
429 if( p_sys
->fd
== -1 )
432 while( i_remain_toread
> 0 )
434 int i_chunk
= i_remain_toread
;
436 if( p_sys
->i_icy_meta
> 0 )
438 if( UINT64_MAX
- i_chunk
< p_sys
->offset
)
439 i_chunk
= i_remain_toread
= UINT64_MAX
- p_sys
->offset
;
441 if( p_sys
->offset
+ i_chunk
> p_sys
->i_icy_offset
)
442 i_chunk
= p_sys
->i_icy_offset
- p_sys
->offset
;
446 if( ReadData( p_access
, &i_read
, &((uint8_t*)p_buffer
)[i_total_read
], i_chunk
) )
450 return -1; /* EINTR / EAGAIN */
454 Disconnect( p_access
);
455 if( p_sys
->b_reconnect
)
457 msg_Dbg( p_access
, "got disconnected, trying to reconnect" );
458 if( Connect( p_access
) )
459 msg_Dbg( p_access
, "reconnection failed" );
466 assert( i_read
>= 0 );
467 p_sys
->offset
+= i_read
;
468 i_total_read
+= i_read
;
469 i_remain_toread
-= i_read
;
471 if( p_sys
->i_icy_meta
> 0 &&
472 p_sys
->offset
== p_sys
->i_icy_offset
)
474 if( ReadICYMeta( p_access
) )
476 p_sys
->i_icy_offset
= p_sys
->offset
+ p_sys
->i_icy_meta
;
483 static int ReadICYMeta( stream_t
*p_access
)
485 access_sys_t
*p_sys
= p_access
->p_sys
;
491 /* Read meta data length */
492 if( ReadData( p_access
, &i_read
, &buffer
, 1 ) )
496 const int i_size
= buffer
<< 4;
497 /* msg_Dbg( p_access, "ICY meta size=%u", i_size); */
499 psz_meta
= malloc( i_size
+ 1 );
500 for( i_read
= 0; i_read
< i_size
; )
503 if( ReadData( p_access
, &i_tmp
, (uint8_t *)&psz_meta
[i_read
], i_size
- i_read
) || i_tmp
<= 0 )
510 psz_meta
[i_read
] = '\0'; /* Just in case */
512 /* msg_Dbg( p_access, "icy-meta=%s", psz_meta ); */
514 /* Now parse the meta */
515 /* Look for StreamTitle= */
516 p
= strcasestr( (char *)psz_meta
, "StreamTitle=" );
519 p
+= strlen( "StreamTitle=" );
520 if( *p
== '\'' || *p
== '"' )
522 char closing
[] = { p
[0], ';', '\0' };
523 char *psz
= strstr( &p
[1], closing
);
525 psz
= strchr( &p
[1], ';' );
527 if( psz
) *psz
= '\0';
531 char *psz
= strchr( &p
[1], ';' );
532 if( psz
) *psz
= '\0';
535 if( !p_sys
->psz_icy_title
||
536 strcmp( p_sys
->psz_icy_title
, &p
[1] ) )
538 free( p_sys
->psz_icy_title
);
539 char *psz_tmp
= strdup( &p
[1] );
540 p_sys
->psz_icy_title
= EnsureUTF8( psz_tmp
);
541 if( !p_sys
->psz_icy_title
)
544 msg_Dbg( p_access
, "New Icy-Title=%s", p_sys
->psz_icy_title
);
545 input_thread_t
*p_input
= p_access
->p_input
;
548 input_item_t
*p_input_item
= input_GetItem( p_access
->p_input
);
550 input_item_SetMeta( p_input_item
, vlc_meta_NowPlaying
, p_sys
->psz_icy_title
);
559 /*****************************************************************************
560 * Seek: close and re-open a connection at the right place
561 *****************************************************************************/
562 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
564 (void) p_access
; (void) i_pos
;
568 /*****************************************************************************
570 *****************************************************************************/
571 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
573 access_sys_t
*p_sys
= p_access
->p_sys
;
580 case STREAM_CAN_SEEK
:
581 case STREAM_CAN_FASTSEEK
:
582 pb_bool
= va_arg( args
, bool* );
585 case STREAM_CAN_PAUSE
:
586 case STREAM_CAN_CONTROL_PACE
:
587 pb_bool
= va_arg( args
, bool* );
592 case STREAM_GET_PTS_DELAY
:
593 pi_64
= va_arg( args
, int64_t * );
594 *pi_64
= INT64_C(1000)
595 * var_InheritInteger( p_access
, "network-caching" );
598 case STREAM_GET_SIZE
:
599 if( !p_sys
->b_has_size
)
601 *va_arg( args
, uint64_t*) = p_sys
->size
;
605 case STREAM_SET_PAUSE_STATE
:
608 case STREAM_GET_CONTENT_TYPE
:
610 char **type
= va_arg( args
, char ** );
612 if( p_sys
->b_icecast
&& p_sys
->psz_mime
== NULL
)
613 *type
= strdup( "audio/mpeg" );
614 else if( !strcasecmp( p_access
->psz_name
, "itpc" ) )
615 *type
= strdup( "application/rss+xml" );
616 else if( !strcasecmp( p_access
->psz_name
, "unsv" ) &&
617 p_sys
->psz_mime
!= NULL
&&
618 !strcasecmp( p_sys
->psz_mime
, "misc/ultravox" ) )
619 /* Grrrr! detect ultravox server and force NSV demuxer */
620 *type
= strdup( "video/nsa" );
621 else if( p_sys
->psz_mime
)
622 *type
= strdup( p_sys
->psz_mime
);
635 /*****************************************************************************
637 *****************************************************************************/
638 static int Connect( stream_t
*p_access
)
640 access_sys_t
*p_sys
= p_access
->p_sys
;
641 vlc_url_t srv
= p_sys
->b_proxy
? p_sys
->proxy
: p_sys
->url
;
645 free( p_sys
->psz_location
);
646 free( p_sys
->psz_mime
);
648 free( p_sys
->psz_icy_genre
);
649 free( p_sys
->psz_icy_name
);
650 free( p_sys
->psz_icy_title
);
652 vlc_http_auth_Init( &p_sys
->auth
);
653 vlc_http_auth_Init( &p_sys
->proxy_auth
);
654 p_sys
->psz_location
= NULL
;
655 p_sys
->psz_mime
= NULL
;
656 p_sys
->i_icy_meta
= 0;
657 p_sys
->i_icy_offset
= 0;
658 p_sys
->psz_icy_name
= NULL
;
659 p_sys
->psz_icy_genre
= NULL
;
660 p_sys
->psz_icy_title
= NULL
;
661 p_sys
->b_has_size
= false;
665 struct vlc_memstream stream
;
667 vlc_memstream_open(&stream
);
669 vlc_memstream_puts(&stream
, "GET ");
671 vlc_memstream_printf( &stream
, "http://%s:%d",
672 p_sys
->url
.psz_host
, p_sys
->url
.i_port
);
673 if( p_sys
->url
.psz_path
== NULL
|| p_sys
->url
.psz_path
[0] == '\0' )
674 vlc_memstream_putc( &stream
, '/' );
676 vlc_memstream_puts( &stream
, p_sys
->url
.psz_path
);
677 if( p_sys
->url
.psz_option
!= NULL
)
678 vlc_memstream_printf( &stream
, "?%s", p_sys
->url
.psz_option
);
679 vlc_memstream_puts( &stream
, " HTTP/1.0\r\n" );
681 vlc_memstream_printf( &stream
, "Host: %s", p_sys
->url
.psz_host
);
682 if( p_sys
->url
.i_port
!= 80 )
683 vlc_memstream_printf( &stream
, ":%d", p_sys
->url
.i_port
);
684 vlc_memstream_puts( &stream
, "\r\n" );
687 vlc_memstream_printf( &stream
, "User-Agent: %s\r\n",
688 p_sys
->psz_user_agent
);
690 if (p_sys
->psz_referrer
)
691 vlc_memstream_printf( &stream
, "Referer: %s\r\n",
692 p_sys
->psz_referrer
);
695 if( p_sys
->url
.psz_username
!= NULL
&& p_sys
->url
.psz_password
!= NULL
)
699 auth
= vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access
),
700 &p_sys
->auth
, "GET", p_sys
->url
.psz_path
,
701 p_sys
->url
.psz_username
, p_sys
->url
.psz_password
);
703 vlc_memstream_printf( &stream
, "Authorization: %s\r\n", auth
);
707 /* Proxy Authentication */
708 if( p_sys
->b_proxy
&& p_sys
->proxy
.psz_username
!= NULL
709 && p_sys
->proxy
.psz_password
!= NULL
)
713 auth
= vlc_http_auth_FormatAuthorizationHeader( VLC_OBJECT(p_access
),
714 &p_sys
->proxy_auth
, "GET", p_sys
->url
.psz_path
,
715 p_sys
->proxy
.psz_username
, p_sys
->proxy
.psz_password
);
717 vlc_memstream_printf( &stream
, "Proxy-Authorization: %s\r\n",
722 /* ICY meta data request */
723 vlc_memstream_puts( &stream
, "Icy-MetaData: 1\r\n" );
725 vlc_memstream_puts( &stream
, "\r\n" );
727 if( vlc_memstream_close( &stream
) )
730 /* Open connection */
731 assert( p_sys
->fd
== -1 ); /* No open sockets (leaking fds is BAD) */
732 p_sys
->fd
= net_ConnectTCP( p_access
, srv
.psz_host
, srv
.i_port
);
733 if( p_sys
->fd
== -1 )
735 msg_Err( p_access
, "cannot connect to %s:%d", srv
.psz_host
, srv
.i_port
);
739 setsockopt (p_sys
->fd
, SOL_SOCKET
, SO_KEEPALIVE
, &(int){ 1 }, sizeof (int));
741 msg_Dbg( p_access
, "sending request:\n%s", stream
.ptr
);
742 val
= net_Write( p_access
, p_sys
->fd
, stream
.ptr
, stream
.length
);
745 if( val
< (ssize_t
)stream
.length
)
747 msg_Err( p_access
, "failed to send request" );
748 Disconnect( p_access
);
753 char *psz
= net_Gets( p_access
, p_sys
->fd
);
756 msg_Err( p_access
, "failed to read answer" );
759 if( !strncmp( psz
, "HTTP/1.", 7 ) )
761 p_sys
->i_code
= atoi( &psz
[9] );
762 msg_Dbg( p_access
, "HTTP answer code %d", p_sys
->i_code
);
764 else if( !strncmp( psz
, "ICY", 3 ) )
766 p_sys
->i_code
= atoi( &psz
[4] );
767 msg_Dbg( p_access
, "ICY answer code %d", p_sys
->i_code
);
768 p_sys
->b_icecast
= true;
769 p_sys
->b_reconnect
= true;
773 msg_Err( p_access
, "invalid HTTP reply '%s'", psz
);
777 /* Authentication error - We'll have to display the dialog */
778 if( p_sys
->i_code
== 401 )
782 /* Other fatal error */
783 else if( p_sys
->i_code
>= 400 )
785 msg_Err( p_access
, "error: %s", psz
);
793 char *p
, *p_trailing
;
795 psz
= net_Gets( p_access
, p_sys
->fd
);
798 msg_Err( p_access
, "failed to read answer" );
802 /* msg_Dbg( p_input, "Line=%s", psz ); */
809 if( ( p
= strchr( psz
, ':' ) ) == NULL
)
811 msg_Err( p_access
, "malformed header line: %s", psz
);
816 p
+= strspn( p
, " \t" );
818 /* trim trailing white space */
819 p_trailing
= p
+ strlen( p
);
823 while( ( *p_trailing
== ' ' || *p_trailing
== '\t' ) && p_trailing
> p
)
830 if( !strcasecmp( psz
, "Content-Length" ) )
832 uint64_t i_size
= (uint64_t)atoll( p
);
833 if(i_size
> p_sys
->size
) {
834 p_sys
->b_has_size
= true;
835 p_sys
->size
= i_size
;
838 else if( !strcasecmp( psz
, "Location" ) )
842 /* This does not follow RFC 2068, but yet if the url is not absolute,
843 * handle it as everyone does. */
846 if( p_sys
->url
.i_port
== 80 )
848 if( asprintf(&psz_new_loc
, "http://%s%s",
849 p_sys
->url
.psz_host
, p
) < 0 )
854 if( asprintf(&psz_new_loc
, "http://%s:%d%s",
855 p_sys
->url
.psz_host
, p_sys
->url
.i_port
, p
) < 0 )
861 psz_new_loc
= strdup( p
);
864 free( p_sys
->psz_location
);
865 p_sys
->psz_location
= psz_new_loc
;
867 else if( !strcasecmp( psz
, "Content-Type" ) )
869 free( p_sys
->psz_mime
);
870 p_sys
->psz_mime
= strdup( p
);
871 msg_Dbg( p_access
, "Content-Type: %s", p_sys
->psz_mime
);
873 else if( !strcasecmp( psz
, "Content-Encoding" ) )
875 msg_Dbg( p_access
, "Content-Encoding: %s", p
);
877 else if( !strcasecmp( psz
, "Server" ) )
879 msg_Dbg( p_access
, "Server: %s", p
);
880 if( !strncasecmp( p
, "Icecast", 7 ) ||
881 !strncasecmp( p
, "Nanocaster", 10 ) )
883 /* Remember if this is Icecast
884 * we need to force demux in this case without breaking
887 /* Let live 365 streams (nanocaster) piggyback on the icecast
888 * routine. They look very similar */
890 p_sys
->b_reconnect
= true;
891 p_sys
->b_icecast
= true;
894 else if( !strcasecmp( psz
, "Icy-MetaInt" ) )
896 msg_Dbg( p_access
, "Icy-MetaInt: %s", p
);
897 p_sys
->i_icy_meta
= atoi( p
);
898 if( p_sys
->i_icy_meta
< 0 )
899 p_sys
->i_icy_meta
= 0;
900 if( p_sys
->i_icy_meta
> 0 )
902 p_sys
->i_icy_offset
= p_sys
->i_icy_meta
;
903 p_sys
->b_icecast
= true;
906 msg_Warn( p_access
, "ICY metaint=%d", p_sys
->i_icy_meta
);
908 else if( !strcasecmp( psz
, "Icy-Name" ) )
910 free( p_sys
->psz_icy_name
);
911 char *psz_tmp
= strdup( p
);
912 p_sys
->psz_icy_name
= EnsureUTF8( psz_tmp
);
913 if( !p_sys
->psz_icy_name
)
916 vlc_xml_decode( p_sys
->psz_icy_name
);
917 msg_Dbg( p_access
, "Icy-Name: %s", p_sys
->psz_icy_name
);
918 input_thread_t
*p_input
= p_access
->p_input
;
921 input_item_t
*p_input_item
= input_GetItem( p_access
->p_input
);
923 input_item_SetMeta( p_input_item
, vlc_meta_Title
, p_sys
->psz_icy_name
);
926 p_sys
->b_icecast
= true; /* be on the safeside. set it here as well. */
927 p_sys
->b_reconnect
= true;
929 else if( !strcasecmp( psz
, "Icy-Genre" ) )
931 free( p_sys
->psz_icy_genre
);
932 char *psz_tmp
= strdup( p
);
933 p_sys
->psz_icy_genre
= EnsureUTF8( psz_tmp
);
934 if( !p_sys
->psz_icy_genre
)
937 vlc_xml_decode( p_sys
->psz_icy_genre
);
938 msg_Dbg( p_access
, "Icy-Genre: %s", p_sys
->psz_icy_genre
);
939 input_thread_t
*p_input
= p_access
->p_input
;
942 input_item_t
*p_input_item
= input_GetItem( p_access
->p_input
);
944 input_item_SetMeta( p_input_item
, vlc_meta_Genre
, p_sys
->psz_icy_genre
);
947 else if( !strncasecmp( psz
, "Icy-Notice", 10 ) )
949 msg_Dbg( p_access
, "Icy-Notice: %s", p
);
951 else if( !strncasecmp( psz
, "icy-", 4 ) ||
952 !strncasecmp( psz
, "ice-", 4 ) ||
953 !strncasecmp( psz
, "x-audiocast", 11 ) )
955 msg_Dbg( p_access
, "Meta-Info: %s: %s", psz
, p
);
957 else if( !strcasecmp( psz
, "www-authenticate" ) )
959 msg_Dbg( p_access
, "Authentication header: %s", p
);
960 vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access
),
963 else if( !strcasecmp( psz
, "proxy-authenticate" ) )
965 msg_Dbg( p_access
, "Proxy authentication header: %s", p
);
966 vlc_http_auth_ParseWwwAuthenticateHeader( VLC_OBJECT(p_access
),
967 &p_sys
->proxy_auth
, p
);
969 else if( !strcasecmp( psz
, "authentication-info" ) )
971 msg_Dbg( p_access
, "Authentication Info header: %s", p
);
972 if( AuthCheckReply( p_access
, p
, &p_sys
->url
, &p_sys
->auth
) )
975 else if( !strcasecmp( psz
, "proxy-authentication-info" ) )
977 msg_Dbg( p_access
, "Proxy Authentication Info header: %s", p
);
978 if( AuthCheckReply( p_access
, p
, &p_sys
->proxy
, &p_sys
->proxy_auth
) )
987 Disconnect( p_access
);
991 /*****************************************************************************
993 *****************************************************************************/
994 static void Disconnect( stream_t
*p_access
)
996 access_sys_t
*p_sys
= p_access
->p_sys
;
999 net_Close(p_sys
->fd
);
1002 vlc_http_auth_Deinit( &p_sys
->auth
);
1003 vlc_http_auth_Deinit( &p_sys
->proxy_auth
);
1006 /*****************************************************************************
1007 * HTTP authentication
1008 *****************************************************************************/
1010 static int AuthCheckReply( stream_t
*p_access
, const char *psz_header
,
1011 vlc_url_t
*p_url
, vlc_http_auth_t
*p_auth
)
1014 vlc_http_auth_ParseAuthenticationInfoHeader( VLC_OBJECT(p_access
),
1018 p_url
->psz_username
,
1019 p_url
->psz_password
);