00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include <stdlib.h>
00030
00031 #include <vlc/vlc.h>
00032 #include <vlc/input.h>
00033
00034 #include "vlc_playlist.h"
00035 #include "vlc_meta.h"
00036 #include "network.h"
00037 #include "vlc_tls.h"
00038
00039
00040
00041
00042 static int Open ( vlc_object_t * );
00043 static void Close( vlc_object_t * );
00044
00045 #define PROXY_TEXT N_("HTTP proxy")
00046 #define PROXY_LONGTEXT N_( \
00047 "You can specify an HTTP proxy to use. It must be of the form " \
00048 "http://[user[:pass]@]myproxy.mydomain:myport/ ; " \
00049 "if empty, the http_proxy environment variable will be tried." )
00050
00051 #define CACHING_TEXT N_("Caching value in ms")
00052 #define CACHING_LONGTEXT N_( \
00053 "Allows you to modify the default caching value for http streams. This " \
00054 "value should be set in millisecond units." )
00055
00056 #define AGENT_TEXT N_("HTTP user agent")
00057 #define AGENT_LONGTEXT N_("Allows you to modify the user agent that will be " \
00058 "used for the connection.")
00059
00060 #define RECONNECT_TEXT N_("Auto re-connect")
00061 #define RECONNECT_LONGTEXT N_("Will automatically attempt a re-connection " \
00062 "in case it was untimely closed.")
00063
00064 #define CONTINUOUS_TEXT N_("Continuous stream")
00065 #define CONTINUOUS_LONGTEXT N_("Enable this option to read a file that is " \
00066 "being constantly updated (for example, a JPG file on a server)")
00067
00068 vlc_module_begin();
00069 set_description( _("HTTP input") );
00070 set_capability( "access2", 0 );
00071 set_shortname( _( "HTTP/HTTPS" ) );
00072 set_category( CAT_INPUT );
00073 set_subcategory( SUBCAT_INPUT_ACCESS );
00074
00075 add_string( "http-proxy", NULL, NULL, PROXY_TEXT, PROXY_LONGTEXT,
00076 VLC_FALSE );
00077 add_integer( "http-caching", 4 * DEFAULT_PTS_DELAY / 1000, NULL,
00078 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
00079 add_string( "http-user-agent", COPYRIGHT_MESSAGE , NULL, AGENT_TEXT,
00080 AGENT_LONGTEXT, VLC_TRUE );
00081 add_bool( "http-reconnect", 0, NULL, RECONNECT_TEXT,
00082 RECONNECT_LONGTEXT, VLC_TRUE );
00083 add_bool( "http-continuous", 0, NULL, CONTINUOUS_TEXT,
00084 CONTINUOUS_LONGTEXT, VLC_TRUE );
00085 add_suppressed_string("http-user");
00086 add_suppressed_string("http-pwd");
00087 add_shortcut( "http" );
00088 add_shortcut( "https" );
00089 add_shortcut( "unsv" );
00090 set_callbacks( Open, Close );
00091 vlc_module_end();
00092
00093
00094
00095
00096 struct access_sys_t
00097 {
00098 int fd;
00099 tls_session_t *p_tls;
00100 v_socket_t *p_vs;
00101
00102
00103 vlc_url_t url;
00104 char *psz_user_agent;
00105
00106
00107 vlc_bool_t b_proxy;
00108 vlc_url_t proxy;
00109
00110
00111 int i_code;
00112 char *psz_protocol;
00113 int i_version;
00114
00115 char *psz_mime;
00116 char *psz_pragma;
00117 char *psz_location;
00118 vlc_bool_t b_mms;
00119 vlc_bool_t b_icecast;
00120 vlc_bool_t b_ssl;
00121
00122 vlc_bool_t b_chunked;
00123 int64_t i_chunk;
00124
00125 int i_icy_meta;
00126 char *psz_icy_name;
00127 char *psz_icy_genre;
00128 char *psz_icy_title;
00129
00130 int i_remaining;
00131
00132 vlc_bool_t b_seekable;
00133 vlc_bool_t b_reconnect;
00134 vlc_bool_t b_continuous;
00135 vlc_bool_t b_pace_control;
00136 };
00137
00138
00139 static int Read( access_t *, uint8_t *, int );
00140 static int Seek( access_t *, int64_t );
00141 static int Control( access_t *, int, va_list );
00142
00143
00144 static int Connect( access_t *, int64_t );
00145 static int Request( access_t *p_access, int64_t i_tell );
00146 static void Disconnect( access_t * );
00147
00148
00149
00150
00151 static int Open( vlc_object_t *p_this )
00152 {
00153 access_t *p_access = (access_t*)p_this;
00154 access_sys_t *p_sys;
00155 char *psz, *p;
00156
00157
00158 p_access->pf_read = Read;
00159 p_access->pf_block = NULL;
00160 p_access->pf_control = Control;
00161 p_access->pf_seek = Seek;
00162 p_access->info.i_update = 0;
00163 p_access->info.i_size = 0;
00164 p_access->info.i_pos = 0;
00165 p_access->info.b_eof = VLC_FALSE;
00166 p_access->info.i_title = 0;
00167 p_access->info.i_seekpoint = 0;
00168 p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
00169 memset( p_sys, 0, sizeof( access_sys_t ) );
00170 p_sys->fd = -1;
00171 p_sys->b_proxy = VLC_FALSE;
00172 p_sys->i_version = 1;
00173 p_sys->b_seekable = VLC_TRUE;
00174 p_sys->psz_mime = NULL;
00175 p_sys->psz_pragma = NULL;
00176 p_sys->b_mms = VLC_FALSE;
00177 p_sys->b_icecast = VLC_FALSE;
00178 p_sys->psz_location = NULL;
00179 p_sys->psz_user_agent = NULL;
00180 p_sys->b_pace_control = VLC_TRUE;
00181 p_sys->b_ssl = VLC_FALSE;
00182 p_sys->p_tls = NULL;
00183 p_sys->p_vs = NULL;
00184 p_sys->i_icy_meta = 0;
00185 p_sys->psz_icy_name = NULL;
00186 p_sys->psz_icy_genre = NULL;
00187 p_sys->psz_icy_title = NULL;
00188 p_sys->i_remaining = 0;
00189
00190
00191 p = psz = strdup( p_access->psz_path );
00192 while( (p = strchr( p, ' ' )) != NULL )
00193 *p = '+';
00194 vlc_UrlParse( &p_sys->url, psz, 0 );
00195 free( psz );
00196
00197 if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
00198 {
00199 msg_Warn( p_access, "invalid host" );
00200 goto error;
00201 }
00202 if( !strncmp( p_access->psz_access, "https", 5 ) )
00203 {
00204
00205 p_sys->b_ssl = VLC_TRUE;
00206 if( p_sys->url.i_port <= 0 )
00207 p_sys->url.i_port = 443;
00208 }
00209 else
00210 {
00211 if( p_sys->url.i_port <= 0 )
00212 p_sys->url.i_port = 80;
00213 }
00214
00215
00216 p_sys->psz_user_agent = var_CreateGetString( p_access, "http-user-agent" );
00217
00218
00219 psz = var_CreateGetString( p_access, "http-proxy" );
00220 if( *psz )
00221 {
00222 p_sys->b_proxy = VLC_TRUE;
00223 vlc_UrlParse( &p_sys->proxy, psz, 0 );
00224 }
00225 #ifdef HAVE_GETENV
00226 else
00227 {
00228 char *psz_proxy = getenv( "http_proxy" );
00229 if( psz_proxy && *psz_proxy )
00230 {
00231 p_sys->b_proxy = VLC_TRUE;
00232 vlc_UrlParse( &p_sys->proxy, psz_proxy, 0 );
00233 }
00234 }
00235 #endif
00236 free( psz );
00237
00238 if( p_sys->b_proxy )
00239 {
00240 if( p_sys->proxy.psz_host == NULL || *p_sys->proxy.psz_host == '\0' )
00241 {
00242 msg_Warn( p_access, "invalid proxy host" );
00243 goto error;
00244 }
00245 if( p_sys->proxy.i_port <= 0 )
00246 {
00247 p_sys->proxy.i_port = 80;
00248 }
00249 }
00250
00251 msg_Dbg( p_access, "http: server='%s' port=%d file='%s",
00252 p_sys->url.psz_host, p_sys->url.i_port, p_sys->url.psz_path );
00253 if( p_sys->b_proxy )
00254 {
00255 msg_Dbg( p_access, " proxy %s:%d", p_sys->proxy.psz_host,
00256 p_sys->proxy.i_port );
00257 }
00258 if( p_sys->url.psz_username && *p_sys->url.psz_username )
00259 {
00260 msg_Dbg( p_access, " user='%s', pwd='%s'",
00261 p_sys->url.psz_username, p_sys->url.psz_password );
00262 }
00263
00264 p_sys->b_reconnect = var_CreateGetBool( p_access, "http-reconnect" );
00265 p_sys->b_continuous = var_CreateGetBool( p_access, "http-continuous" );
00266
00267
00268 if( Connect( p_access, 0 ) )
00269 {
00270
00271 p_sys->i_version = 0;
00272
00273 if( p_access->b_die ||
00274 Connect( p_access, 0 ) )
00275 {
00276 goto error;
00277 }
00278 }
00279
00280 if( ( p_sys->i_code == 301 || p_sys->i_code == 302 ||
00281 p_sys->i_code == 303 || p_sys->i_code == 307 ) &&
00282 p_sys->psz_location && *p_sys->psz_location )
00283 {
00284 playlist_t * p_playlist;
00285 input_item_t *p_input_item;
00286
00287 msg_Dbg( p_access, "redirection to %s", p_sys->psz_location );
00288
00289
00290 if( strncmp( p_sys->psz_location, "http", 4 )
00291 || ( ( p_sys->psz_location[4] != ':' )
00292 && strncmp( p_sys->psz_location + 4, "s:", 2 ) ) )
00293 {
00294 msg_Err( p_access, "insecure redirection ignored" );
00295 goto error;
00296 }
00297
00298 p_playlist = vlc_object_find( p_access, VLC_OBJECT_PLAYLIST,
00299 FIND_ANYWHERE );
00300 if( !p_playlist )
00301 {
00302 msg_Err( p_access, "redirection failed: can't find playlist" );
00303 goto error;
00304 }
00305
00306
00307 vlc_mutex_lock( &p_playlist->object_lock );
00308 p_input_item = &p_playlist->status.p_item->input;
00309 vlc_mutex_lock( &p_input_item->lock );
00310 free( p_input_item->psz_uri );
00311 free( p_access->psz_path );
00312 p_input_item->psz_uri = strdup( p_sys->psz_location );
00313 p_access->psz_path = strdup( p_sys->psz_location );
00314 vlc_mutex_unlock( &p_input_item->lock );
00315 vlc_mutex_unlock( &p_playlist->object_lock );
00316 vlc_object_release( p_playlist );
00317
00318
00319 vlc_UrlClean( &p_sys->url );
00320 vlc_UrlClean( &p_sys->proxy );
00321 if( p_sys->psz_mime ) free( p_sys->psz_mime );
00322 if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
00323 if( p_sys->psz_location ) free( p_sys->psz_location );
00324 if( p_sys->psz_user_agent ) free( p_sys->psz_user_agent );
00325
00326 Disconnect( p_access );
00327 free( p_sys );
00328
00329
00330 return Open( p_this );
00331 }
00332
00333 if( p_sys->b_mms )
00334 {
00335 msg_Dbg( p_access, "This is actually a live mms server, BAIL" );
00336 goto error;
00337 }
00338
00339 if( !strcmp( p_sys->psz_protocol, "ICY" ) || p_sys->b_icecast )
00340 {
00341 if( p_sys->psz_mime && strcasecmp( p_sys->psz_mime, "application/ogg" ) )
00342 {
00343 if( !strcasecmp( p_sys->psz_mime, "video/nsv" ) ||
00344 !strcasecmp( p_sys->psz_mime, "video/nsa" ) )
00345 p_access->psz_demux = strdup( "nsv" );
00346 else if( !strcasecmp( p_sys->psz_mime, "audio/aac" ) ||
00347 !strcasecmp( p_sys->psz_mime, "audio/aacp" ) )
00348 p_access->psz_demux = strdup( "m4a" );
00349 else if( !strcasecmp( p_sys->psz_mime, "audio/mpeg" ) )
00350 p_access->psz_demux = strdup( "mp3" );
00351
00352 msg_Info( p_access, "Raw-audio server found, %s demuxer selected",
00353 p_access->psz_demux );
00354
00355 #if 0
00356
00357
00358 p_sys->b_pace_control = VLC_FALSE;
00359 #endif
00360 }
00361 else if( !p_sys->psz_mime )
00362 {
00363
00364 p_access->psz_demux = strdup( "mp3" );
00365 }
00366
00367 }
00368 else if( !strcasecmp( p_access->psz_access, "unsv" ) &&
00369 p_sys->psz_mime &&
00370 !strcasecmp( p_sys->psz_mime, "misc/ultravox" ) )
00371 {
00372
00373 p_access->psz_demux = strdup( "nsv" );
00374 }
00375
00376 if( p_sys->b_reconnect ) msg_Dbg( p_access, "auto re-connect enabled" );
00377
00378
00379 var_Create( p_access, "http-caching", VLC_VAR_INTEGER |VLC_VAR_DOINHERIT );
00380
00381 return VLC_SUCCESS;
00382
00383 error:
00384 vlc_UrlClean( &p_sys->url );
00385 vlc_UrlClean( &p_sys->proxy );
00386 if( p_sys->psz_mime ) free( p_sys->psz_mime );
00387 if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
00388 if( p_sys->psz_location ) free( p_sys->psz_location );
00389 if( p_sys->psz_user_agent ) free( p_sys->psz_user_agent );
00390
00391 Disconnect( p_access );
00392 free( p_sys );
00393 return VLC_EGENERIC;
00394 }
00395
00396
00397
00398
00399 static void Close( vlc_object_t *p_this )
00400 {
00401 access_t *p_access = (access_t*)p_this;
00402 access_sys_t *p_sys = p_access->p_sys;
00403
00404 vlc_UrlClean( &p_sys->url );
00405 vlc_UrlClean( &p_sys->proxy );
00406
00407 if( p_sys->psz_mime ) free( p_sys->psz_mime );
00408 if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
00409 if( p_sys->psz_location ) free( p_sys->psz_location );
00410
00411 if( p_sys->psz_icy_name ) free( p_sys->psz_icy_name );
00412 if( p_sys->psz_icy_genre ) free( p_sys->psz_icy_genre );
00413 if( p_sys->psz_icy_title ) free( p_sys->psz_icy_title );
00414
00415 if( p_sys->psz_user_agent ) free( p_sys->psz_user_agent );
00416
00417 Disconnect( p_access );
00418 free( p_sys );
00419 }
00420
00421
00422
00423
00424
00425 static int ReadICYMeta( access_t *p_access );
00426 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
00427 {
00428 access_sys_t *p_sys = p_access->p_sys;
00429 int i_read;
00430
00431 if( p_sys->fd < 0 )
00432 {
00433 p_access->info.b_eof = VLC_TRUE;
00434 return 0;
00435 }
00436
00437 if( p_access->info.i_size > 0 &&
00438 i_len + p_access->info.i_pos > p_access->info.i_size )
00439 {
00440 if( ( i_len = p_access->info.i_size - p_access->info.i_pos ) == 0 )
00441 {
00442 p_access->info.b_eof = VLC_TRUE;
00443 return 0;
00444 }
00445 }
00446
00447 if( p_sys->b_chunked )
00448 {
00449 if( p_sys->i_chunk < 0 )
00450 {
00451 p_access->info.b_eof = VLC_TRUE;
00452 return 0;
00453 }
00454
00455 if( p_sys->i_chunk <= 0 )
00456 {
00457 char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, p_sys->p_vs );
00458
00459 if( psz == NULL )
00460 {
00461 msg_Dbg( p_access, "failed reading chunk-header line" );
00462 return -1;
00463 }
00464 p_sys->i_chunk = strtoll( psz, NULL, 16 );
00465 free( psz );
00466
00467 if( p_sys->i_chunk <= 0 )
00468 {
00469 p_sys->i_chunk = -1;
00470 p_access->info.b_eof = VLC_TRUE;
00471 return 0;
00472 }
00473 }
00474
00475 if( i_len > p_sys->i_chunk )
00476 {
00477 i_len = p_sys->i_chunk;
00478 }
00479 }
00480
00481 if( p_sys->b_continuous && i_len > p_sys->i_remaining )
00482 {
00483
00484 int i_new_len = p_sys->i_remaining;
00485 if( i_new_len == 0 )
00486 {
00487 Request( p_access, 0 );
00488 i_read = Read( p_access, p_buffer, i_len );
00489 return i_read;
00490 }
00491 i_len = i_new_len;
00492 }
00493
00494 if( p_sys->i_icy_meta > 0 && p_access->info.i_pos > 0 )
00495 {
00496 int64_t i_next = p_sys->i_icy_meta -
00497 p_access->info.i_pos % p_sys->i_icy_meta;
00498
00499 if( i_next == p_sys->i_icy_meta )
00500 {
00501 if( ReadICYMeta( p_access ) )
00502 {
00503 p_access->info.b_eof = VLC_TRUE;
00504 return -1;
00505 }
00506 }
00507 if( i_len > i_next )
00508 i_len = i_next;
00509 }
00510
00511 i_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, p_buffer, i_len, VLC_FALSE );
00512
00513 if( i_read > 0 )
00514 {
00515 p_access->info.i_pos += i_read;
00516
00517 if( p_sys->b_chunked )
00518 {
00519 p_sys->i_chunk -= i_read;
00520 if( p_sys->i_chunk <= 0 )
00521 {
00522
00523 char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, p_sys->p_vs );
00524 if( psz ) free( psz );
00525 }
00526 }
00527 }
00528 else if( i_read == 0 )
00529 {
00530
00531
00532
00533
00534
00535
00536 if( p_sys->b_continuous )
00537 {
00538 Request( p_access, 0 );
00539 p_sys->b_continuous = VLC_FALSE;
00540 i_read = Read( p_access, p_buffer, i_len );
00541 p_sys->b_continuous = VLC_TRUE;
00542 }
00543 Disconnect( p_access );
00544 if( p_sys->b_reconnect )
00545 {
00546 msg_Dbg( p_access, "got disconnected, trying to reconnect" );
00547 if( Connect( p_access, p_access->info.i_pos ) )
00548 {
00549 msg_Dbg( p_access, "reconnection failed" );
00550 }
00551 else
00552 {
00553 p_sys->b_reconnect = VLC_FALSE;
00554 i_read = Read( p_access, p_buffer, i_len );
00555 p_sys->b_reconnect = VLC_TRUE;
00556 }
00557 }
00558
00559 if( i_read == 0 ) p_access->info.b_eof = VLC_TRUE;
00560 }
00561
00562 if( p_sys->b_continuous )
00563 {
00564 p_sys->i_remaining -= i_read;
00565 }
00566
00567 return i_read;
00568 }
00569
00570 static int ReadICYMeta( access_t *p_access )
00571 {
00572 access_sys_t *p_sys = p_access->p_sys;
00573
00574 uint8_t buffer;
00575 char *p, *psz_meta;
00576 int i_read;
00577
00578
00579 i_read = net_Read( p_access, p_sys->fd, p_sys->p_vs, &buffer, 1,
00580 VLC_TRUE );
00581 if( i_read <= 0 )
00582 return VLC_EGENERIC;
00583 if( buffer == 0 )
00584 return VLC_SUCCESS;
00585
00586 i_read = buffer << 4;
00587
00588
00589 psz_meta = malloc( i_read + 1 );
00590 if( net_Read( p_access, p_sys->fd, p_sys->p_vs,
00591 (uint8_t *)psz_meta, i_read, VLC_TRUE ) != i_read )
00592 return VLC_EGENERIC;
00593
00594 psz_meta[i_read] = '\0';
00595
00596
00597
00598
00599
00600 p = strcasestr( (char *)psz_meta, "StreamTitle=" );
00601 if( p )
00602 {
00603 p += strlen( "StreamTitle=" );
00604 if( *p == '\'' || *p == '"' )
00605 {
00606 char *psz = strchr( &p[1], p[0] );
00607 if( !psz )
00608 psz = strchr( &p[1], ';' );
00609
00610 if( psz ) *psz = '\0';
00611 }
00612 else
00613 {
00614 char *psz = strchr( &p[1], ';' );
00615 if( psz ) *psz = '\0';
00616 }
00617
00618 if( !p_sys->psz_icy_title ||
00619 strcmp( p_sys->psz_icy_title, &p[1] ) )
00620 {
00621 if( p_sys->psz_icy_title )
00622 free( p_sys->psz_icy_title );
00623 p_sys->psz_icy_title = strdup( &p[1] );
00624 p_access->info.i_update |= INPUT_UPDATE_META;
00625
00626 msg_Dbg( p_access, "New Title=%s", p_sys->psz_icy_title );
00627 }
00628 }
00629 free( psz_meta );
00630
00631 return VLC_SUCCESS;
00632 }
00633
00634
00635
00636
00637 static int Seek( access_t *p_access, int64_t i_pos )
00638 {
00639 msg_Dbg( p_access, "trying to seek to "I64Fd, i_pos );
00640
00641 Disconnect( p_access );
00642
00643 if( Connect( p_access, i_pos ) )
00644 {
00645 msg_Err( p_access, "seek failed" );
00646 p_access->info.b_eof = VLC_TRUE;
00647 return VLC_EGENERIC;
00648 }
00649 return VLC_SUCCESS;
00650 }
00651
00652
00653
00654
00655 static int Control( access_t *p_access, int i_query, va_list args )
00656 {
00657 access_sys_t *p_sys = p_access->p_sys;
00658 vlc_bool_t *pb_bool;
00659 int *pi_int;
00660 int64_t *pi_64;
00661 vlc_meta_t **pp_meta;
00662
00663 switch( i_query )
00664 {
00665
00666 case ACCESS_CAN_SEEK:
00667 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00668 *pb_bool = p_sys->b_seekable;
00669 break;
00670 case ACCESS_CAN_FASTSEEK:
00671 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00672 *pb_bool = VLC_FALSE;
00673 break;
00674 case ACCESS_CAN_PAUSE:
00675 case ACCESS_CAN_CONTROL_PACE:
00676 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00677
00678 #if 0
00679
00680 *pb_bool = p_sys->b_pace_control;
00681 #endif
00682 *pb_bool = VLC_TRUE;
00683 break;
00684
00685
00686 case ACCESS_GET_MTU:
00687 pi_int = (int*)va_arg( args, int * );
00688 *pi_int = 0;
00689 break;
00690
00691 case ACCESS_GET_PTS_DELAY:
00692 pi_64 = (int64_t*)va_arg( args, int64_t * );
00693 *pi_64 = (int64_t)var_GetInteger( p_access, "http-caching" ) * 1000;
00694 break;
00695
00696
00697 case ACCESS_SET_PAUSE_STATE:
00698 break;
00699
00700 case ACCESS_GET_META:
00701 pp_meta = (vlc_meta_t**)va_arg( args, vlc_meta_t** );
00702 *pp_meta = vlc_meta_New();
00703
00704 if( p_sys->psz_icy_name )
00705 vlc_meta_Add( *pp_meta, VLC_META_TITLE,
00706 p_sys->psz_icy_name );
00707 if( p_sys->psz_icy_genre )
00708 vlc_meta_Add( *pp_meta, VLC_META_GENRE,
00709 p_sys->psz_icy_genre );
00710 if( p_sys->psz_icy_title )
00711 vlc_meta_Add( *pp_meta, VLC_META_NOW_PLAYING,
00712 p_sys->psz_icy_title );
00713 break;
00714
00715 case ACCESS_GET_TITLE_INFO:
00716 case ACCESS_SET_TITLE:
00717 case ACCESS_SET_SEEKPOINT:
00718 case ACCESS_SET_PRIVATE_ID_STATE:
00719 return VLC_EGENERIC;
00720
00721 default:
00722 msg_Warn( p_access, "unimplemented query in control" );
00723 return VLC_EGENERIC;
00724
00725 }
00726 return VLC_SUCCESS;
00727 }
00728
00729
00730
00731
00732 static int Connect( access_t *p_access, int64_t i_tell )
00733 {
00734 access_sys_t *p_sys = p_access->p_sys;
00735 vlc_url_t srv = p_sys->b_proxy ? p_sys->proxy : p_sys->url;
00736
00737
00738 if( p_sys->psz_location ) free( p_sys->psz_location );
00739 if( p_sys->psz_mime ) free( p_sys->psz_mime );
00740 if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
00741
00742 if( p_sys->psz_icy_genre ) free( p_sys->psz_icy_genre );
00743 if( p_sys->psz_icy_name ) free( p_sys->psz_icy_name );
00744 if( p_sys->psz_icy_title ) free( p_sys->psz_icy_title );
00745
00746
00747 p_sys->psz_location = NULL;
00748 p_sys->psz_mime = NULL;
00749 p_sys->psz_pragma = NULL;
00750 p_sys->b_mms = VLC_FALSE;
00751 p_sys->b_chunked = VLC_FALSE;
00752 p_sys->i_chunk = 0;
00753 p_sys->i_icy_meta = 0;
00754 p_sys->psz_icy_name = NULL;
00755 p_sys->psz_icy_genre = NULL;
00756 p_sys->psz_icy_title = NULL;
00757
00758 p_access->info.i_size = 0;
00759 p_access->info.i_pos = i_tell;
00760 p_access->info.b_eof = VLC_FALSE;
00761
00762
00763
00764 p_sys->fd = net_OpenTCP( p_access, srv.psz_host, srv.i_port );
00765 if( p_sys->fd < 0 )
00766 {
00767 msg_Err( p_access, "cannot connect to %s:%d", srv.psz_host, srv.i_port );
00768 return VLC_EGENERIC;
00769 }
00770
00771
00772 if( p_sys->b_ssl == VLC_TRUE )
00773 {
00774
00775 if( p_sys->b_proxy )
00776 {
00777 char *psz;
00778 unsigned i_status = 0;
00779
00780 if( p_sys->i_version == 0 )
00781 {
00782
00783 Disconnect( p_access );
00784 return VLC_EGENERIC;
00785 }
00786
00787 net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
00788 "CONNECT %s:%d HTTP/1.%d\r\nHost: %s:%d\r\n\r\n",
00789 p_sys->url.psz_host, p_sys->url.i_port,
00790 p_sys->i_version,
00791 p_sys->url.psz_host, p_sys->url.i_port);
00792
00793 psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
00794 if( psz == NULL )
00795 {
00796 msg_Err( p_access, "cannot establish HTTP/TLS tunnel" );
00797 Disconnect( p_access );
00798 return VLC_EGENERIC;
00799 }
00800
00801 sscanf( psz, "HTTP/%*u.%*u %3u", &i_status );
00802 free( psz );
00803
00804 if( ( i_status / 100 ) != 2 )
00805 {
00806 msg_Err( p_access, "HTTP/TLS tunnel through proxy denied" );
00807 Disconnect( p_access );
00808 return VLC_EGENERIC;
00809 }
00810
00811 do
00812 {
00813 psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, NULL );
00814 if( psz == NULL )
00815 {
00816 msg_Err( p_access, "HTTP proxy connection failed" );
00817 Disconnect( p_access );
00818 return VLC_EGENERIC;
00819 }
00820
00821 if( *psz == '\0' )
00822 i_status = 0;
00823
00824 free( psz );
00825 }
00826 while( i_status );
00827 }
00828
00829
00830 p_sys->p_tls = tls_ClientCreate( VLC_OBJECT(p_access), p_sys->fd,
00831 srv.psz_host );
00832 if( p_sys->p_tls == NULL )
00833 {
00834 msg_Err( p_access, "cannot establish HTTP/TLS session" );
00835 Disconnect( p_access );
00836 return VLC_EGENERIC;
00837 }
00838 p_sys->p_vs = &p_sys->p_tls->sock;
00839 }
00840
00841 return Request( p_access, i_tell );
00842 }
00843
00844
00845 static int Request( access_t *p_access, int64_t i_tell )
00846 {
00847 access_sys_t *p_sys = p_access->p_sys;
00848 char *psz ;
00849 v_socket_t *pvs = p_sys->p_vs;
00850
00851 if( p_sys->b_proxy )
00852 {
00853 if( p_sys->url.psz_path )
00854 {
00855 net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
00856 "GET http://%s:%d%s HTTP/1.%d\r\n",
00857 p_sys->url.psz_host, p_sys->url.i_port,
00858 p_sys->url.psz_path, p_sys->i_version );
00859 }
00860 else
00861 {
00862 net_Printf( VLC_OBJECT(p_access), p_sys->fd, NULL,
00863 "GET http://%s:%d/ HTTP/1.%d\r\n",
00864 p_sys->url.psz_host, p_sys->url.i_port,
00865 p_sys->i_version );
00866 }
00867 }
00868 else
00869 {
00870 char *psz_path = p_sys->url.psz_path;
00871 if( !psz_path || !*psz_path )
00872 {
00873 psz_path = "/";
00874 }
00875 if( p_sys->url.i_port != 80)
00876 {
00877 net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
00878 "GET %s HTTP/1.%d\r\nHost: %s:%d\r\n",
00879 psz_path, p_sys->i_version, p_sys->url.psz_host,
00880 p_sys->url.i_port );
00881 }
00882 else
00883 {
00884 net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
00885 "GET %s HTTP/1.%d\r\nHost: %s\r\n",
00886 psz_path, p_sys->i_version, p_sys->url.psz_host );
00887 }
00888 }
00889
00890 net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "User-Agent: %s\r\n",
00891 p_sys->psz_user_agent );
00892
00893 if( p_sys->i_version == 1 )
00894 {
00895 net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
00896 "Range: bytes="I64Fd"-\r\n", i_tell );
00897 }
00898
00899
00900 if( p_sys->url.psz_username && *p_sys->url.psz_username )
00901 {
00902 char *buf;
00903 char *b64;
00904
00905 asprintf( &buf, "%s:%s", p_sys->url.psz_username,
00906 p_sys->url.psz_password ? p_sys->url.psz_password : "" );
00907
00908 b64 = vlc_b64_encode( buf );
00909 free( buf );
00910
00911 net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
00912 "Authorization: Basic %s\r\n", b64 );
00913 free( b64 );
00914 }
00915
00916
00917 if( p_sys->proxy.psz_username && *p_sys->proxy.psz_username )
00918 {
00919 char *buf;
00920 char *b64;
00921
00922 asprintf( &buf, "%s:%s", p_sys->proxy.psz_username,
00923 p_sys->proxy.psz_password ? p_sys->proxy.psz_password : "" );
00924
00925 b64 = vlc_b64_encode( buf );
00926 free( buf );
00927
00928 net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
00929 "Proxy-Authorization: Basic %s\r\n", b64 );
00930 free( b64 );
00931 }
00932
00933
00934 net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "Icy-MetaData: 1\r\n" );
00935
00936
00937 if( p_sys->b_continuous && p_sys->i_version == 1 )
00938 {
00939 net_Printf( VLC_OBJECT( p_access ), p_sys->fd, pvs,
00940 "Connection: keep-alive\r\n" );
00941 }
00942 else
00943 {
00944 net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs,
00945 "Connection: Close\r\n");
00946 p_sys->b_continuous = VLC_FALSE;
00947 }
00948
00949 if( net_Printf( VLC_OBJECT(p_access), p_sys->fd, pvs, "\r\n" ) < 0 )
00950 {
00951 msg_Err( p_access, "failed to send request" );
00952 Disconnect( p_access );
00953 return VLC_EGENERIC;
00954 }
00955
00956
00957 if( ( psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, pvs ) ) == NULL )
00958 {
00959 msg_Err( p_access, "failed to read answer" );
00960 goto error;
00961 }
00962 if( !strncmp( psz, "HTTP/1.", 7 ) )
00963 {
00964 p_sys->psz_protocol = "HTTP";
00965 p_sys->i_code = atoi( &psz[9] );
00966 }
00967 else if( !strncmp( psz, "ICY", 3 ) )
00968 {
00969 p_sys->psz_protocol = "ICY";
00970 p_sys->i_code = atoi( &psz[4] );
00971 p_sys->b_reconnect = VLC_TRUE;
00972 }
00973 else
00974 {
00975 msg_Err( p_access, "invalid HTTP reply '%s'", psz );
00976 free( psz );
00977 goto error;
00978 }
00979 msg_Dbg( p_access, "protocol '%s' answer code %d",
00980 p_sys->psz_protocol, p_sys->i_code );
00981 if( !strcmp( p_sys->psz_protocol, "ICY" ) )
00982 {
00983 p_sys->b_seekable = VLC_FALSE;
00984 }
00985 if( p_sys->i_code != 206 )
00986 {
00987 p_sys->b_seekable = VLC_FALSE;
00988 }
00989 if( p_sys->i_code >= 400 )
00990 {
00991 msg_Err( p_access, "error: %s", psz );
00992 free( psz );
00993 goto error;
00994 }
00995 free( psz );
00996
00997 for( ;; )
00998 {
00999 char *psz = net_Gets( VLC_OBJECT(p_access), p_sys->fd, pvs );
01000 char *p;
01001
01002 if( psz == NULL )
01003 {
01004 msg_Err( p_access, "failed to read answer" );
01005 goto error;
01006 }
01007
01008
01009 if( *psz == '\0' )
01010 {
01011 free( psz );
01012 break;
01013 }
01014
01015
01016 if( ( p = strchr( psz, ':' ) ) == NULL )
01017 {
01018 msg_Err( p_access, "malformed header line: %s", psz );
01019 free( psz );
01020 goto error;
01021 }
01022 *p++ = '\0';
01023 while( *p == ' ' ) p++;
01024
01025 if( !strcasecmp( psz, "Content-Length" ) )
01026 {
01027 if( p_sys->b_continuous )
01028 {
01029 p_access->info.i_size = -1;
01030 msg_Dbg( p_access, "this frame size="I64Fd, atoll(p ) );
01031 p_sys->i_remaining = atoll( p );
01032 }
01033 else
01034 {
01035 p_access->info.i_size = i_tell + atoll( p );
01036 msg_Dbg( p_access, "stream size="I64Fd, p_access->info.i_size );
01037 }
01038 }
01039 else if( !strcasecmp( psz, "Location" ) )
01040 {
01041 if( p_sys->psz_location ) free( p_sys->psz_location );
01042 p_sys->psz_location = strdup( p );
01043 }
01044 else if( !strcasecmp( psz, "Content-Type" ) )
01045 {
01046 if( p_sys->psz_mime ) free( p_sys->psz_mime );
01047 p_sys->psz_mime = strdup( p );
01048 msg_Dbg( p_access, "Content-Type: %s", p_sys->psz_mime );
01049 }
01050 else if( !strcasecmp( psz, "Pragma" ) )
01051 {
01052 if( !strcasecmp( psz, "Pragma: features" ) )
01053 p_sys->b_mms = VLC_TRUE;
01054 if( p_sys->psz_pragma ) free( p_sys->psz_pragma );
01055 p_sys->psz_pragma = strdup( p );
01056 msg_Dbg( p_access, "Pragma: %s", p_sys->psz_pragma );
01057 }
01058 else if( !strcasecmp( psz, "Server" ) )
01059 {
01060 msg_Dbg( p_access, "Server: %s", p );
01061 if( !strncasecmp( p, "Icecast", 7 ) ||
01062 !strncasecmp( p, "Nanocaster", 10 ) )
01063 {
01064
01065
01066
01067
01068
01069
01070
01071 p_sys->b_reconnect = VLC_TRUE;
01072 p_sys->b_pace_control = VLC_FALSE;
01073 p_sys->b_icecast = VLC_TRUE;
01074 }
01075 }
01076 else if( !strcasecmp( psz, "Transfer-Encoding" ) )
01077 {
01078 msg_Dbg( p_access, "Transfer-Encoding: %s", p );
01079 if( !strncasecmp( p, "chunked", 7 ) )
01080 {
01081 p_sys->b_chunked = VLC_TRUE;
01082 }
01083 }
01084 else if( !strcasecmp( psz, "Icy-MetaInt" ) )
01085 {
01086 msg_Dbg( p_access, "Icy-MetaInt: %s", p );
01087 p_sys->i_icy_meta = atoi( p );
01088 if( p_sys->i_icy_meta < 0 )
01089 p_sys->i_icy_meta = 0;
01090
01091 msg_Warn( p_access, "ICY metaint=%d", p_sys->i_icy_meta );
01092 }
01093 else if( !strcasecmp( psz, "Icy-Name" ) )
01094 {
01095 if( p_sys->psz_icy_name ) free( p_sys->psz_icy_name );
01096 p_sys->psz_icy_name = strdup( p );
01097 msg_Dbg( p_access, "Icy-Name: %s", p_sys->psz_icy_name );
01098
01099 p_sys->b_icecast = VLC_TRUE;
01100 p_sys->b_reconnect = VLC_TRUE;
01101 p_sys->b_pace_control = VLC_FALSE;
01102 }
01103 else if( !strcasecmp( psz, "Icy-Genre" ) )
01104 {
01105 if( p_sys->psz_icy_genre ) free( p_sys->psz_icy_genre );
01106 p_sys->psz_icy_genre = strdup( p );
01107 msg_Dbg( p_access, "Icy-Genre: %s", p_sys->psz_icy_genre );
01108 }
01109 else if( !strncasecmp( psz, "Icy-Notice", 10 ) )
01110 {
01111 msg_Dbg( p_access, "Icy-Notice: %s", p );
01112 }
01113 else if( !strncasecmp( psz, "icy-", 4 ) ||
01114 !strncasecmp( psz, "ice-", 4 ) ||
01115 !strncasecmp( psz, "x-audiocast", 11 ) )
01116 {
01117 msg_Dbg( p_access, "Meta-Info: %s: %s", psz, p );
01118 }
01119
01120 free( psz );
01121 }
01122 return VLC_SUCCESS;
01123
01124 error:
01125 Disconnect( p_access );
01126 return VLC_EGENERIC;
01127 }
01128
01129
01130
01131
01132 static void Disconnect( access_t *p_access )
01133 {
01134 access_sys_t *p_sys = p_access->p_sys;
01135
01136 if( p_sys->p_tls != NULL)
01137 {
01138 tls_ClientDelete( p_sys->p_tls );
01139 p_sys->p_tls = NULL;
01140 p_sys->p_vs = NULL;
01141 }
01142 if( p_sys->fd != -1)
01143 {
01144 net_Close(p_sys->fd);
01145 p_sys->fd = -1;
01146 }
01147
01148 }