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 #include <stdlib.h>
00029
00030 #include <vlc/vlc.h>
00031 #include <vlc/input.h>
00032
00033 #include "network.h"
00034 #if defined( UNDER_CE )
00035 # include <winsock.h>
00036 #elif defined( WIN32 )
00037 # include <winsock2.h>
00038 #else
00039 # include <sys/socket.h>
00040 #endif
00041
00042
00043
00044
00045 static int Open ( vlc_object_t * );
00046 static void Close( vlc_object_t * );
00047
00048 #define CACHING_TEXT N_("Caching value in ms")
00049 #define CACHING_LONGTEXT N_( \
00050 "Allows you to modify the default caching value for FTP streams. This " \
00051 "value should be set in millisecond units." )
00052 #define USER_TEXT N_("FTP user name")
00053 #define USER_LONGTEXT N_("Allows you to modify the user name that will " \
00054 "be used for the connection.")
00055 #define PASS_TEXT N_("FTP password")
00056 #define PASS_LONGTEXT N_("Allows you to modify the password that will be " \
00057 "used for the connection.")
00058 #define ACCOUNT_TEXT N_("FTP account")
00059 #define ACCOUNT_LONGTEXT N_("Allows you to modify the account that will be " \
00060 "used for the connection.")
00061
00062 vlc_module_begin();
00063 set_shortname( "FTP" );
00064 set_description( _("FTP input") );
00065 set_capability( "access2", 0 );
00066 set_category( CAT_INPUT );
00067 set_subcategory( SUBCAT_INPUT_ACCESS );
00068 add_integer( "ftp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL,
00069 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
00070 add_string( "ftp-user", "anonymous", NULL, USER_TEXT, USER_LONGTEXT,
00071 VLC_FALSE );
00072 add_string( "ftp-pwd", "[email protected]", NULL, PASS_TEXT,
00073 PASS_LONGTEXT, VLC_FALSE );
00074 add_string( "ftp-account", "anonymous", NULL, ACCOUNT_TEXT,
00075 ACCOUNT_LONGTEXT, VLC_FALSE );
00076 add_shortcut( "ftp" );
00077 set_callbacks( Open, Close );
00078 vlc_module_end();
00079
00080
00081
00082
00083 static int Read( access_t *, uint8_t *, int );
00084 static int Seek( access_t *, int64_t );
00085 static int Control( access_t *, int, va_list );
00086
00087 struct access_sys_t
00088 {
00089 vlc_url_t url;
00090
00091 int fd_cmd;
00092 int fd_data;
00093
00094 char sz_epsv_ip[NI_MAXNUMERICHOST];
00095 };
00096
00097 static int ftp_SendCommand( access_t *, char *, ... );
00098 static int ftp_ReadCommand( access_t *, int *, char ** );
00099 static int ftp_StartStream( access_t *, int64_t );
00100 static int ftp_StopStream ( access_t *);
00101
00102 static int Connect( access_t *p_access, access_sys_t *p_sys )
00103 {
00104 int fd, i_answer;
00105 char *psz;
00106
00107
00108 msg_Dbg( p_access, "waiting for connection..." );
00109 p_sys->fd_cmd = fd = net_OpenTCP( p_access, p_sys->url.psz_host,
00110 p_sys->url.i_port );
00111 if( fd < 0 )
00112 {
00113 msg_Err( p_access, "failed to connect with server" );
00114 return -1;
00115 }
00116
00117 for( ;; )
00118 {
00119 if( ftp_ReadCommand( p_access, &i_answer, NULL ) != 1 )
00120 {
00121 break;
00122 }
00123 }
00124 if( i_answer / 100 != 2 )
00125 {
00126 msg_Err( p_access, "connection rejected" );
00127 return -1;
00128 }
00129
00130 msg_Dbg( p_access, "connection accepted (%d)", i_answer );
00131
00132 psz = var_CreateGetString( p_access, "ftp-user" );
00133 if( ftp_SendCommand( p_access, "USER %s", psz ) < 0 ||
00134 ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
00135 {
00136 free( psz );
00137 return -1;
00138 }
00139 free( psz );
00140
00141 switch( i_answer / 100 )
00142 {
00143 case 2:
00144 msg_Dbg( p_access, "user accepted" );
00145 break;
00146 case 3:
00147 msg_Dbg( p_access, "password needed" );
00148 psz = var_CreateGetString( p_access, "ftp-pwd" );
00149 if( ftp_SendCommand( p_access, "PASS %s", psz ) < 0 ||
00150 ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
00151 {
00152 free( psz );
00153 return -1;
00154 }
00155 free( psz );
00156
00157 switch( i_answer / 100 )
00158 {
00159 case 2:
00160 msg_Dbg( p_access, "password accepted" );
00161 break;
00162 case 3:
00163 msg_Dbg( p_access, "account needed" );
00164 psz = var_CreateGetString( p_access, "ftp-account" );
00165 if( ftp_SendCommand( p_access, "ACCT %s",
00166 psz ) < 0 ||
00167 ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
00168 {
00169 free( psz );
00170 return -1;
00171 }
00172 free( psz );
00173
00174 if( i_answer / 100 != 2 )
00175 {
00176 msg_Err( p_access, "account rejected" );
00177 return -1;
00178 }
00179 msg_Dbg( p_access, "account accepted" );
00180 break;
00181
00182 default:
00183 msg_Err( p_access, "password rejected" );
00184 return -1;
00185 }
00186 break;
00187 default:
00188 msg_Err( p_access, "user rejected" );
00189 return -1;
00190 }
00191
00192 return 0;
00193 }
00194
00195
00196
00197
00198 static int Open( vlc_object_t *p_this )
00199 {
00200 access_t *p_access = (access_t*)p_this;
00201 access_sys_t *p_sys;
00202 char *psz;
00203
00204 int i_answer;
00205 char *psz_arg;
00206
00207
00208 p_access->pf_read = Read;
00209 p_access->pf_block = NULL;
00210 p_access->pf_seek = Seek;
00211 p_access->pf_control = Control;
00212 p_access->info.i_update = 0;
00213 p_access->info.i_size = 0;
00214 p_access->info.i_pos = 0;
00215 p_access->info.b_eof = VLC_FALSE;
00216 p_access->info.i_title = 0;
00217 p_access->info.i_seekpoint = 0;
00218 p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
00219 memset( p_sys, 0, sizeof( access_sys_t ) );
00220 p_sys->fd_cmd = -1;
00221 p_sys->fd_data = -1;
00222
00223
00224 psz = p_access->psz_path;
00225 while( *psz == '/' )
00226 {
00227 psz++;
00228 }
00229 vlc_UrlParse( &p_sys->url, psz, 0 );
00230
00231 if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
00232 {
00233 msg_Err( p_access, "invalid server name" );
00234 goto exit_error;
00235 }
00236 if( p_sys->url.i_port <= 0 )
00237 {
00238 p_sys->url.i_port = 21;
00239 }
00240
00241
00242
00243
00244 if( *p_sys->url.psz_path == '/' )
00245 p_sys->url.psz_path++;
00246
00247 if( Connect( p_access, p_sys ) < 0 )
00248 goto exit_error;
00249
00250
00251 if( ftp_SendCommand( p_access, "EPSV ALL" ) < 0 )
00252 {
00253 msg_Err( p_access, "cannot request extended passive mode" );
00254 return -1;
00255 }
00256
00257 if( ftp_ReadCommand( p_access, &i_answer, NULL ) == 2 )
00258 {
00259 if( net_GetPeerAddress( p_sys->fd_cmd, p_sys->sz_epsv_ip, NULL ) )
00260 goto exit_error;
00261 }
00262 else
00263 {
00264
00265
00266
00267
00268
00269 net_Close( p_sys->fd_cmd );
00270 p_sys->fd_cmd = -1;
00271 *p_sys->sz_epsv_ip = '\0';
00272
00273 if( ( p_sys->fd_cmd = Connect( p_access, p_sys ) ) < 0 )
00274 goto exit_error;
00275
00276 msg_Info( p_access, "FTP Extended passive mode disabled" );
00277 }
00278
00279
00280 if( ftp_SendCommand( p_access, "TYPE I" ) < 0 ||
00281 ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 )
00282 {
00283 msg_Err( p_access, "cannot set binary transfer mode" );
00284 goto exit_error;
00285 }
00286
00287
00288 if( ftp_SendCommand( p_access, "SIZE %s", p_sys->url.psz_path ) < 0 ||
00289 ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 )
00290 {
00291 msg_Err( p_access, "cannot get file size" );
00292 goto exit_error;
00293 }
00294 p_access->info.i_size = atoll( &psz_arg[4] );
00295 free( psz_arg );
00296 msg_Dbg( p_access, "file size: "I64Fd, p_access->info.i_size );
00297
00298
00299 if( ftp_StartStream( p_access, 0 ) < 0 )
00300 {
00301 msg_Err( p_access, "cannot retrieve file" );
00302 goto exit_error;
00303 }
00304
00305
00306 var_Create( p_access, "ftp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
00307
00308 return VLC_SUCCESS;
00309
00310 exit_error:
00311 if( p_sys->fd_cmd >= 0 )
00312 net_Close( p_sys->fd_cmd );
00313 vlc_UrlClean( &p_sys->url );
00314 free( p_sys );
00315 return VLC_EGENERIC;
00316 }
00317
00318
00319
00320
00321 static void Close( vlc_object_t *p_this )
00322 {
00323 access_t *p_access = (access_t*)p_this;
00324 access_sys_t *p_sys = p_access->p_sys;
00325
00326 msg_Dbg( p_access, "stopping stream" );
00327 ftp_StopStream( p_access );
00328
00329 if( ftp_SendCommand( p_access, "QUIT" ) < 0 )
00330 {
00331 msg_Warn( p_access, "cannot quit" );
00332 }
00333 else
00334 {
00335 ftp_ReadCommand( p_access, NULL, NULL );
00336 }
00337 net_Close( p_sys->fd_cmd );
00338
00339
00340 vlc_UrlClean( &p_sys->url );
00341 free( p_sys );
00342 }
00343
00344
00345
00346
00347 static int Seek( access_t *p_access, int64_t i_pos )
00348 {
00349 if( i_pos < 0 )
00350 {
00351 return VLC_EGENERIC;
00352 }
00353 msg_Dbg( p_access, "seeking to "I64Fd, i_pos );
00354
00355 ftp_StopStream( p_access );
00356 if( ftp_StartStream( p_access, i_pos ) < 0 )
00357 {
00358 p_access->info.b_eof = VLC_TRUE;
00359 return VLC_EGENERIC;
00360 }
00361
00362 p_access->info.b_eof = VLC_FALSE;
00363 p_access->info.i_pos = i_pos;
00364
00365 return VLC_SUCCESS;
00366 }
00367
00368
00369
00370
00371 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
00372 {
00373 access_sys_t *p_sys = p_access->p_sys;
00374 int i_read;
00375
00376 if( p_access->info.b_eof )
00377 return 0;
00378
00379 i_read = net_Read( p_access, p_sys->fd_data, NULL, p_buffer, i_len,
00380 VLC_FALSE );
00381 if( i_read == 0 )
00382 p_access->info.b_eof = VLC_TRUE;
00383 else if( i_read > 0 )
00384 p_access->info.i_pos += i_read;
00385
00386 return i_read;
00387 }
00388
00389
00390
00391
00392 static int Control( access_t *p_access, int i_query, va_list args )
00393 {
00394 vlc_bool_t *pb_bool;
00395 int *pi_int;
00396 int64_t *pi_64;
00397 vlc_value_t val;
00398
00399 switch( i_query )
00400 {
00401
00402 case ACCESS_CAN_SEEK:
00403 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00404 *pb_bool = VLC_TRUE;
00405 break;
00406 case ACCESS_CAN_FASTSEEK:
00407 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00408 *pb_bool = VLC_FALSE;
00409 break;
00410 case ACCESS_CAN_PAUSE:
00411 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00412 *pb_bool = VLC_TRUE;
00413 break;
00414 case ACCESS_CAN_CONTROL_PACE:
00415 pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00416 *pb_bool = VLC_TRUE;
00417 break;
00418
00419
00420 case ACCESS_GET_MTU:
00421 pi_int = (int*)va_arg( args, int * );
00422 *pi_int = 0;
00423 break;
00424
00425 case ACCESS_GET_PTS_DELAY:
00426 pi_64 = (int64_t*)va_arg( args, int64_t * );
00427 var_Get( p_access, "ftp-caching", &val );
00428 *pi_64 = (int64_t)var_GetInteger( p_access, "ftp-caching" ) * I64C(1000);
00429 break;
00430
00431
00432 case ACCESS_SET_PAUSE_STATE:
00433
00434 break;
00435
00436 case ACCESS_GET_TITLE_INFO:
00437 case ACCESS_SET_TITLE:
00438 case ACCESS_SET_SEEKPOINT:
00439 case ACCESS_SET_PRIVATE_ID_STATE:
00440 return VLC_EGENERIC;
00441
00442 default:
00443 msg_Warn( p_access, "unimplemented query in control" );
00444 return VLC_EGENERIC;
00445
00446 }
00447 return VLC_SUCCESS;
00448 }
00449
00450
00451
00452
00453 static int ftp_SendCommand( access_t *p_access, char *psz_fmt, ... )
00454 {
00455 access_sys_t *p_sys = p_access->p_sys;
00456 va_list args;
00457 char *psz_cmd;
00458
00459 va_start( args, psz_fmt );
00460 vasprintf( &psz_cmd, psz_fmt, args );
00461 va_end( args );
00462
00463 msg_Dbg( p_access, "ftp_SendCommand:\"%s\"", psz_cmd);
00464 if( net_Printf( VLC_OBJECT(p_access), p_sys->fd_cmd, NULL, "%s\r\n",
00465 psz_cmd ) < 0 )
00466 {
00467 msg_Err( p_access, "failed to send command" );
00468 return VLC_EGENERIC;
00469 }
00470 return VLC_SUCCESS;
00471 }
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488 static int ftp_ReadCommand( access_t *p_access,
00489 int *pi_answer, char **ppsz_answer )
00490 {
00491 access_sys_t *p_sys = p_access->p_sys;
00492 char *psz_line;
00493 int i_answer;
00494
00495 psz_line = net_Gets( p_access, p_sys->fd_cmd, NULL );
00496 msg_Dbg( p_access, "answer=%s", psz_line );
00497 if( psz_line == NULL || strlen( psz_line ) < 3 )
00498 {
00499 msg_Err( p_access, "cannot get answer" );
00500 if( psz_line ) free( psz_line );
00501 if( pi_answer ) *pi_answer = 500;
00502 if( ppsz_answer ) *ppsz_answer = NULL;
00503 return -1;
00504 }
00505
00506 if( psz_line[3] == '-' )
00507 {
00508 char end[4];
00509
00510 memcpy( end, psz_line, 3 );
00511 end[3] = ' ';
00512
00513 for( ;; )
00514 {
00515 char *psz_tmp = net_Gets( p_access, p_sys->fd_cmd, NULL );
00516
00517 if( psz_tmp == NULL )
00518 break;
00519
00520 if( !strncmp( psz_tmp, end, 4 ) )
00521 {
00522 free( psz_tmp );
00523 break;
00524 }
00525 free( psz_tmp );
00526 }
00527 }
00528
00529 i_answer = atoi( psz_line );
00530
00531 if( pi_answer ) *pi_answer = i_answer;
00532 if( ppsz_answer )
00533 {
00534 *ppsz_answer = psz_line;
00535 }
00536 else
00537 {
00538 free( psz_line );
00539 }
00540 return( i_answer / 100 );
00541 }
00542
00543 static int ftp_StartStream( access_t *p_access, off_t i_start )
00544 {
00545 access_sys_t *p_sys = p_access->p_sys;
00546
00547 char psz_ipv4[16], *psz_ip;
00548 int i_answer;
00549 char *psz_arg, *psz_parser;
00550 int i_port;
00551
00552 psz_ip = p_sys->sz_epsv_ip;
00553
00554 if( ( ftp_SendCommand( p_access, *psz_ip ? "EPSV" : "PASV" ) < 0 )
00555 || ( ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 ) )
00556 {
00557 msg_Err( p_access, "cannot set passive mode" );
00558 return VLC_EGENERIC;
00559 }
00560
00561 psz_parser = strchr( psz_arg, '(' );
00562 if( psz_parser == NULL )
00563 {
00564 free( psz_arg );
00565 msg_Err( p_access, "cannot parse passive mode response" );
00566 return VLC_EGENERIC;
00567 }
00568
00569 if( psz_ip != NULL )
00570 {
00571 char psz_fmt[7] = "(|||%u";
00572 psz_fmt[1] = psz_fmt[2] = psz_fmt[3] = psz_parser[1];
00573
00574 if( sscanf( psz_parser, psz_fmt, &i_port ) < 1 )
00575 {
00576 free( psz_arg );
00577 msg_Err( p_access, "cannot parse passive mode response" );
00578 return VLC_EGENERIC;
00579 }
00580 }
00581 else
00582 {
00583 unsigned a1, a2, a3, a4, p1, p2;
00584
00585 if( ( sscanf( psz_parser, "(%u,%u,%u,%u,%u,%u", &a1, &a2, &a3, &a4,
00586 &p1, &p2 ) < 6 ) || ( a1 > 255 ) || ( a2 > 255 )
00587 || ( a3 > 255 ) || ( a4 > 255 ) || ( p1 > 255 ) || ( p2 > 255 ) )
00588 {
00589 free( psz_arg );
00590 msg_Err( p_access, "cannot parse passive mode response" );
00591 return VLC_EGENERIC;
00592 }
00593
00594 sprintf( psz_ipv4, "%u.%u.%u.%u", a1, a2, a3, a4 );
00595 psz_ip = psz_ipv4;
00596 i_port = (p1 << 8) | p2;
00597 }
00598 free( psz_arg );
00599
00600 msg_Dbg( p_access, "ip:%s port:%d", psz_ip, i_port );
00601
00602 if( ftp_SendCommand( p_access, "TYPE I" ) < 0 ||
00603 ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 )
00604 {
00605 msg_Err( p_access, "cannot set binary transfer mode" );
00606 return VLC_EGENERIC;
00607 }
00608
00609 if( i_start > 0 )
00610 {
00611 if( ftp_SendCommand( p_access, "REST "I64Fu, i_start ) < 0 ||
00612 ftp_ReadCommand( p_access, &i_answer, NULL ) > 3 )
00613 {
00614 msg_Err( p_access, "cannot set restart point" );
00615 return VLC_EGENERIC;
00616 }
00617 }
00618
00619 msg_Dbg( p_access, "waiting for data connection..." );
00620 p_sys->fd_data = net_OpenTCP( p_access, psz_ip, i_port );
00621 if( p_sys->fd_data < 0 )
00622 {
00623 msg_Err( p_access, "failed to connect with server" );
00624 return VLC_EGENERIC;
00625 }
00626 msg_Dbg( p_access, "connection with \"%s:%d\" successful",
00627 psz_ip, i_port );
00628
00629
00630 if( ftp_SendCommand( p_access, "RETR %s", p_sys->url.psz_path ) < 0 ||
00631 ftp_ReadCommand( p_access, &i_answer, NULL ) > 2 )
00632 {
00633 msg_Err( p_access, "cannot retreive file" );
00634 return VLC_EGENERIC;
00635 }
00636 return VLC_SUCCESS;
00637 }
00638
00639 static int ftp_StopStream ( access_t *p_access )
00640 {
00641 access_sys_t *p_sys = p_access->p_sys;
00642
00643 int i_answer;
00644
00645 if( ftp_SendCommand( p_access, "ABOR" ) < 0 )
00646 {
00647 msg_Warn( p_access, "cannot abort file" );
00648 if( p_sys->fd_data > 0 )
00649 net_Close( p_sys->fd_data );
00650 p_sys->fd_data = -1;
00651 return VLC_EGENERIC;
00652 }
00653 if( p_sys->fd_data > 0 )
00654 {
00655 net_Close( p_sys->fd_data );
00656 p_sys->fd_data = -1;
00657 ftp_ReadCommand( p_access, &i_answer, NULL );
00658 }
00659 ftp_ReadCommand( p_access, &i_answer, NULL );
00660
00661 return VLC_SUCCESS;
00662 }
00663