X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fftp.c;h=246ebe8e6de0c84c376ebc1dc30a88e1ab6e22f5;hb=d18bfd92bb33df6e590efa02a55121ee75529b4f;hp=cf79b155c338c12f6c9b91473bea78f6a8402a25;hpb=9a5db9e20c642201ac9d3474a8c62188b81c9105;p=vlc diff --git a/modules/access/ftp.c b/modules/access/ftp.c index cf79b155c3..246ebe8e6d 100644 --- a/modules/access/ftp.c +++ b/modules/access/ftp.c @@ -1,10 +1,11 @@ /***************************************************************************** - * ftp.c: + * ftp.c: FTP input module ***************************************************************************** - * Copyright (C) 2001, 2002 VideoLAN - * $Id: ftp.c,v 1.6 2003/02/04 10:07:40 massiot Exp $ + * Copyright (C) 2001-2005 the VideoLAN team + * $Id$ * - * Authors: Laurent Aimar + * Authors: Laurent Aimar - original code + * Rémi Denis-Courmont - EPSV support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,378 +19,294 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ #include -#include -#include -#include -#include -#include #include #include -#ifdef HAVE_SYS_TIME_H -# include -#endif - -#ifdef HAVE_UNISTD_H -# include -#elif defined( _MSC_VER ) && defined( _WIN32 ) -# include -#endif - -#ifdef WIN32 -# include -# include -# ifndef IN_MULTICAST -# define IN_MULTICAST(a) IN_CLASSD(a) -# endif -#else -# include -# include -# if HAVE_ARPA_INET_H -# include -# elif defined( SYS_BEOS ) -# include -# endif -#endif - #include "network.h" - -/***************************************************************************** - * Local prototypes - *****************************************************************************/ -static int Open ( vlc_object_t * ); -static void Close ( vlc_object_t * ); - -static int Read ( input_thread_t * p_input, byte_t * p_buffer, - size_t i_len ); -static void Seek ( input_thread_t *, off_t ); -static int SetProgram ( input_thread_t *, pgrm_descriptor_t * ); - - -static ssize_t NetRead ( input_thread_t *, input_socket_t *, byte_t *, size_t ); -static void NetClose( input_thread_t *, input_socket_t *); - -static int ftp_SendCommand( input_thread_t *, char *, ... ); -static int ftp_ReadCommand( input_thread_t *, int *, char ** ); -static int ftp_StartStream( input_thread_t *, off_t ); -static int ftp_StopStream ( input_thread_t *); +#include "vlc_url.h" /***************************************************************************** * Module descriptor *****************************************************************************/ -#define CACHING_TEXT N_("caching value in ms") +static int Open ( vlc_object_t * ); +static void Close( vlc_object_t * ); + +#define CACHING_TEXT N_("Caching value in ms") #define CACHING_LONGTEXT N_( \ - "Allows you to modify the default caching value for ftp streams. This " \ - "value should be set in miliseconds units." ) + "Allows you to modify the default caching value for FTP streams. This " \ + "value should be set in millisecond units." ) +#define USER_TEXT N_("FTP user name") +#define USER_LONGTEXT N_("Allows you to modify the user name that will " \ + "be used for the connection.") +#define PASS_TEXT N_("FTP password") +#define PASS_LONGTEXT N_("Allows you to modify the password that will be " \ + "used for the connection.") +#define ACCOUNT_TEXT N_("FTP account") +#define ACCOUNT_LONGTEXT N_("Allows you to modify the account that will be " \ + "used for the connection.") vlc_module_begin(); - set_description( _("ftp access module") ); - set_capability( "access", 0 ); - add_category_hint( "stream", NULL ); - add_integer( "ftp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL, - CACHING_TEXT, CACHING_LONGTEXT ); - add_string( "ftp-user", "anonymous", NULL, "ftp user name", "ftp user name" ); - add_string( "ftp-pwd", "anonymous@dummy.org", NULL, "ftp password", "ftp password, be careful with that option..." ); - add_string( "ftp-account", "anonymous", NULL, "ftp account", "ftp account" ); + set_shortname( "FTP" ); + set_description( _("FTP input") ); + set_capability( "access2", 0 ); + set_category( CAT_INPUT ); + set_subcategory( SUBCAT_INPUT_ACCESS ); + add_integer( "ftp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL, + CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE ); + add_string( "ftp-user", "anonymous", NULL, USER_TEXT, USER_LONGTEXT, + VLC_FALSE ); + add_string( "ftp-pwd", "anonymous@dummy.org", NULL, PASS_TEXT, + PASS_LONGTEXT, VLC_FALSE ); + add_string( "ftp-account", "anonymous", NULL, ACCOUNT_TEXT, + ACCOUNT_LONGTEXT, VLC_FALSE ); add_shortcut( "ftp" ); set_callbacks( Open, Close ); vlc_module_end(); -/* url: [/]host[:port][/path] */ -typedef struct url_s -{ - char *psz_server_addr; - int i_server_port; - - char *psz_bind_addr; - int i_bind_port; - - char *psz_path; - - /* private */ - char *psz_private; -} url_t; - -static void ftp_ParseURL( url_t *, char * ); - -#define FREE( p ) if( p ) free( p ) +/***************************************************************************** + * Local prototypes + *****************************************************************************/ +static int Read( access_t *, uint8_t *, int ); +static int Seek( access_t *, int64_t ); +static int Control( access_t *, int, va_list ); -typedef struct access_s +struct access_sys_t { - input_socket_t socket_cmd; - input_socket_t socket_data; - - url_t url; /* connect to this server */ - - off_t i_filesize; + vlc_url_t url; - int i_eos; + int fd_cmd; + int fd_data; + + char sz_epsv_ip[NI_MAXNUMERICHOST]; +}; -} access_t; - - -/**************************************************************************** - **************************************************************************** - ******************* ******************* - ******************* Main functions ******************* - ******************* ******************* - **************************************************************************** - ****************************************************************************/ +static int ftp_SendCommand( access_t *, char *, ... ); +static int ftp_ReadCommand( access_t *, int *, char ** ); +static int ftp_StartStream( access_t *, int64_t ); +static int ftp_StopStream ( access_t *); -/**************************************************************************** - * Open: connect to ftp server and ask for file - ****************************************************************************/ -static int Open( vlc_object_t *p_this ) +static int Connect( access_t *p_access, access_sys_t *p_sys ) { - input_thread_t *p_input = (input_thread_t*)p_this; - - access_t *p_access; - char *psz_network; - - module_t *p_network; - network_socket_t socket_desc; - url_t *p_url; - - int i_answer; - char *psz_user, *psz_pwd, *psz_account; - - char *psz_arg; + int fd, i_answer; + char *psz; - /* *** allocate p_access_data *** */ - p_input->p_access_data = - (void*)p_access = malloc( sizeof( access_t ) ); - memset( p_access, 0, sizeof( access_t ) ); - p_url = &p_access->url; - - /* *** Parse URL and get server addr/port and path *** */ - ftp_ParseURL( p_url, p_input->psz_name ); - - if( p_url->psz_server_addr == NULL || - !( *p_url->psz_server_addr ) ) - { - FREE( p_url->psz_private ); - msg_Err( p_input, "invalid server name" ); - return( -1 ); - } - if( p_url->i_server_port == 0 ) + /* *** Open a TCP connection with server *** */ + msg_Dbg( p_access, "waiting for connection..." ); + p_sys->fd_cmd = fd = net_ConnectTCP( p_access, p_sys->url.psz_host, + p_sys->url.i_port ); + if( fd < 0 ) { - p_url->i_server_port = 21; /* default port */ + msg_Err( p_access, "failed to connect with server" ); + return -1; } - /* 2: look at ip version ipv4/ipv6 */ - psz_network = ""; - if( config_GetInt( p_input, "ipv4" ) ) - { - psz_network = "ipv4"; - } - else if( config_GetInt( p_input, "ipv6" ) ) - { - psz_network = "ipv6"; - } - - /* 3: Open a TCP connection with server *** */ - msg_Dbg( p_input, "waiting for connection..." ); - socket_desc.i_type = NETWORK_TCP; - socket_desc.psz_server_addr = p_url->psz_server_addr; - socket_desc.i_server_port = p_url->i_server_port; - socket_desc.psz_bind_addr = ""; - socket_desc.i_bind_port = 0; - p_input->p_private = (void*)&socket_desc; - if( !( p_network = module_Need( p_input, "network", psz_network ) ) ) - { - msg_Err( p_input, "failed to connect with server" ); - FREE( p_access->url.psz_private ); - FREE( p_input->p_access_data ); - return( -1 ); - } - module_Unneed( p_input, p_network ); - p_access->socket_cmd.i_handle = socket_desc.i_handle; - p_input->i_mtu = socket_desc.i_mtu; - msg_Dbg( p_input, - "connection with \"%s:%d\" successful", - p_url->psz_server_addr, - p_url->i_server_port ); - - for( ;; ) { - if( ftp_ReadCommand( p_input, &i_answer, NULL ) < 0) - { - msg_Err( p_input, "failed to get answer" ); - goto exit_error; - } - if( i_answer / 100 != 1 ) + if( ftp_ReadCommand( p_access, &i_answer, NULL ) != 1 ) { break; } } - if( i_answer / 100 != 2 ) { - msg_Err( p_input, "connection rejected" ); - goto exit_error; - } - else - { - msg_Dbg( p_input, "connection accepted (%d)", i_answer ); + msg_Err( p_access, "connection rejected" ); + return -1; } - psz_user = config_GetPsz( p_input, "ftp-user" ); - if( ftp_SendCommand( p_input, "USER %s", psz_user ) < 0 ) - { - FREE( psz_user ); - goto exit_error; - } - FREE( psz_user ); + msg_Dbg( p_access, "connection accepted (%d)", i_answer ); - if( ftp_ReadCommand( p_input, &i_answer, NULL ) < 0) + psz = var_CreateGetString( p_access, "ftp-user" ); + if( ftp_SendCommand( p_access, "USER %s", psz ) < 0 || + ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 ) { - msg_Err( p_input, "failed to get answer" ); - goto exit_error; + free( psz ); + return -1; } + free( psz ); + switch( i_answer / 100 ) { case 2: - msg_Dbg( p_input, "user accepted" ); + msg_Dbg( p_access, "user accepted" ); break; case 3: - msg_Dbg( p_input, "password needed" ); - psz_pwd = config_GetPsz( p_input, "ftp-pwd" ); - if( ftp_SendCommand( p_input, "PASS %s", psz_pwd ) < 0 ) - { - FREE( psz_pwd ); - goto exit_error; - } - FREE( psz_pwd ); - if( ftp_ReadCommand( p_input, &i_answer, NULL ) < 0) + msg_Dbg( p_access, "password needed" ); + psz = var_CreateGetString( p_access, "ftp-pwd" ); + if( ftp_SendCommand( p_access, "PASS %s", psz ) < 0 || + ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 ) { - msg_Err( p_input, "failed to get answer" ); - goto exit_error; + free( psz ); + return -1; } + free( psz ); + switch( i_answer / 100 ) { case 2: - msg_Dbg( p_input, "password accepted" ); + msg_Dbg( p_access, "password accepted" ); break; case 3: - msg_Dbg( p_input, "account needed" ); - psz_account = config_GetPsz( p_input, "ftp-account" ); - if( ftp_SendCommand( p_input, "ACCT %s", psz_account ) < 0 ) - { - FREE( psz_account ); - goto exit_error; - } - FREE( psz_account ); - if( ftp_ReadCommand( p_input, &i_answer, NULL ) < 0) + msg_Dbg( p_access, "account needed" ); + psz = var_CreateGetString( p_access, "ftp-account" ); + if( ftp_SendCommand( p_access, "ACCT %s", + psz ) < 0 || + ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 ) { - msg_Err( p_input, "failed to get answer" ); - goto exit_error; + free( psz ); + return -1; } + free( psz ); + if( i_answer / 100 != 2 ) { - msg_Err( p_input, "account rejected" ); - goto exit_error; - } - else - { - msg_Dbg( p_input, "account accepted" ); + msg_Err( p_access, "account rejected" ); + return -1; } + msg_Dbg( p_access, "account accepted" ); break; + default: - msg_Err( p_input, "password rejected" ); - goto exit_error; + msg_Err( p_access, "password rejected" ); + return -1; } break; default: - msg_Err( p_input, "user rejected" ); - goto exit_error; + msg_Err( p_access, "user rejected" ); + return -1; } - if( ftp_SendCommand( p_input, "TYPE I" ) < 0 ) + return 0; +} + +/**************************************************************************** + * Open: connect to ftp server and ask for file + ****************************************************************************/ +static int Open( vlc_object_t *p_this ) +{ + access_t *p_access = (access_t*)p_this; + access_sys_t *p_sys; + char *psz; + + int i_answer; + char *psz_arg; + + /* Init p_access */ + p_access->pf_read = Read; + p_access->pf_block = NULL; + p_access->pf_seek = Seek; + p_access->pf_control = Control; + p_access->info.i_update = 0; + p_access->info.i_size = 0; + p_access->info.i_pos = 0; + p_access->info.b_eof = VLC_FALSE; + p_access->info.i_title = 0; + p_access->info.i_seekpoint = 0; + p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) ); + memset( p_sys, 0, sizeof( access_sys_t ) ); + p_sys->fd_cmd = -1; + p_sys->fd_data = -1; + + /* *** Parse URL and get server addr/port and path *** */ + psz = p_access->psz_path; + while( *psz == '/' ) { - msg_Err( p_input, "cannot set binary transfert mode" ); - goto exit_error; - } - if( ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 ) - { - msg_Err( p_input, "cannot set binary transfert mode" ); - goto exit_error; + psz++; } + vlc_UrlParse( &p_sys->url, psz, 0 ); - /* get size */ - if( ftp_SendCommand( p_input, "SIZE %s", p_url->psz_path ) < 0 ) + if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' ) { - msg_Err( p_input, "cannot get file size" ); + msg_Err( p_access, "invalid server name" ); goto exit_error; } - if( ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 ) + if( p_sys->url.i_port <= 0 ) { - msg_Err( p_input, "cannot get file size" ); - goto exit_error; + p_sys->url.i_port = 21; /* default port */ } -#ifdef HAVE_ATOLL - p_access->i_filesize = atoll( psz_arg + 4 ); -#else - { - int64_t i_size = 0; - char *psz_parser = psz_arg + 4; + /* FTP URLs are relative to user's default directory (RFC1738) + For absolute path use ftp://foo.bar//usr/local/etc/filename */ - while( *psz_parser == ' ' ) psz_parser++; + if( *p_sys->url.psz_path == '/' ) + p_sys->url.psz_path++; - while( psz_parser[0] >= '0' && psz_parser[0] <= '9' ) - { - i_size *= 10; - i_size += psz_parser[0] - '0'; - } - p_access->i_filesize = i_size; + if( Connect( p_access, p_sys ) < 0 ) + goto exit_error; + + /* Extended passive mode */ + if( ftp_SendCommand( p_access, "EPSV ALL" ) < 0 ) + { + msg_Err( p_access, "cannot request extended passive mode" ); + return -1; + } + + if( ftp_ReadCommand( p_access, &i_answer, NULL ) == 2 ) + { + if( net_GetPeerAddress( p_sys->fd_cmd, p_sys->sz_epsv_ip, NULL ) ) + goto exit_error; } -#endif + else + { + /* If ESPV ALL fails, we fallback to PASV. + * We have to restart the connection in case there is a NAT that + * understands EPSV ALL in the way, and hence won't allow PASV on + * the initial connection. + */ + net_Close( p_sys->fd_cmd ); + p_sys->fd_cmd = -1; + *p_sys->sz_epsv_ip = '\0'; - msg_Dbg( p_input, "file size: "I64Fd, p_access->i_filesize ); - FREE( psz_arg ); + if( ( p_sys->fd_cmd = Connect( p_access, p_sys ) ) < 0 ) + goto exit_error; - if( ftp_StartStream( p_input, 0 ) < 0 ) + msg_Info( p_access, "FTP Extended passive mode disabled" ); + } + + /* binary mode */ + if( ftp_SendCommand( p_access, "TYPE I" ) < 0 || + ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 ) { - msg_Err( p_input, "cannot retrieve file" ); + msg_Err( p_access, "cannot set binary transfer mode" ); goto exit_error; } - /* *** set exported functions *** */ - p_input->pf_read = Read; - p_input->pf_seek = Seek; - p_input->pf_set_program = SetProgram; - p_input->pf_set_area = NULL; - p_input->p_private = NULL; + /* get size */ + if( ftp_SendCommand( p_access, "SIZE %s", p_sys->url.psz_path ) < 0 || + ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 ) + { + msg_Err( p_access, "cannot get file size" ); + goto exit_error; + } + p_access->info.i_size = atoll( &psz_arg[4] ); + free( psz_arg ); + msg_Dbg( p_access, "file size: "I64Fd, p_access->info.i_size ); - /* *** finished to set some variable *** */ - vlc_mutex_lock( &p_input->stream.stream_lock ); - p_input->stream.b_pace_control = 1; - p_input->stream.p_selected_area->i_tell = 0; - p_input->stream.b_seekable = 1; - p_input->stream.b_connected = 1; - p_input->stream.p_selected_area->i_size = p_access->i_filesize; - p_input->stream.i_method = INPUT_METHOD_NETWORK; - vlc_mutex_unlock( &p_input->stream.stream_lock ); + /* Start the 'stream' */ + if( ftp_StartStream( p_access, 0 ) < 0 ) + { + msg_Err( p_access, "cannot retrieve file" ); + goto exit_error; + } /* Update default_pts to a suitable value for ftp access */ - p_input->i_pts_delay = config_GetInt( p_input, "ftp-caching" ) * 1000; + var_Create( p_access, "ftp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); - return( 0 ); + return VLC_SUCCESS; exit_error: - NetClose( p_input, &p_access->socket_cmd ); - FREE( p_access->url.psz_private ); - FREE( p_input->p_access_data ); - return( -1 ); + if( p_sys->fd_cmd >= 0 ) + net_Close( p_sys->fd_cmd ); + vlc_UrlClean( &p_sys->url ); + free( p_sys ); + return VLC_EGENERIC; } /***************************************************************************** @@ -397,115 +314,156 @@ exit_error: *****************************************************************************/ static void Close( vlc_object_t *p_this ) { - input_thread_t *p_input = (input_thread_t *)p_this; - access_t *p_access = (access_t*)p_input->p_access_data; + access_t *p_access = (access_t*)p_this; + access_sys_t *p_sys = p_access->p_sys; - msg_Dbg( p_input, "stopping stream" ); - ftp_StopStream( p_input ); + msg_Dbg( p_access, "stopping stream" ); + ftp_StopStream( p_access ); - if( ftp_SendCommand( p_input, "QUIT" ) < 0 ) + if( ftp_SendCommand( p_access, "QUIT" ) < 0 ) { - msg_Err( p_input, "cannot quit" ); + msg_Warn( p_access, "cannot quit" ); } else { - ftp_ReadCommand( p_input, NULL, NULL ); + ftp_ReadCommand( p_access, NULL, NULL ); } - - - NetClose( p_input, &p_access->socket_cmd ); + net_Close( p_sys->fd_cmd ); /* free memory */ - FREE( p_access->url.psz_private ); -} - -/***************************************************************************** - * SetProgram: do nothing - *****************************************************************************/ -static int SetProgram( input_thread_t * p_input, - pgrm_descriptor_t * p_program ) -{ - return( 0 ); + vlc_UrlClean( &p_sys->url ); + free( p_sys ); } /***************************************************************************** * Seek: try to go at the right place *****************************************************************************/ -static void Seek( input_thread_t * p_input, off_t i_pos ) +static int Seek( access_t *p_access, int64_t i_pos ) { - //access_t *p_access = (access_t*)p_input->p_access_data; if( i_pos < 0 ) { - return; + return VLC_EGENERIC; } - vlc_mutex_lock( &p_input->stream.stream_lock ); + msg_Dbg( p_access, "seeking to "I64Fd, i_pos ); - msg_Dbg( p_input, "seeking to "I64Fd, i_pos ); + ftp_StopStream( p_access ); + if( ftp_StartStream( p_access, i_pos ) < 0 ) + { + p_access->info.b_eof = VLC_TRUE; + return VLC_EGENERIC; + } - ftp_StopStream( p_input ); - ftp_StartStream( p_input, i_pos ); + p_access->info.b_eof = VLC_FALSE; + p_access->info.i_pos = i_pos; - p_input->stream.p_selected_area->i_tell = i_pos; - vlc_mutex_unlock( &p_input->stream.stream_lock ); + return VLC_SUCCESS; } -static int Read ( input_thread_t * p_input, byte_t * p_buffer, - size_t i_len ) +/***************************************************************************** + * Read: + *****************************************************************************/ +static int Read( access_t *p_access, uint8_t *p_buffer, int i_len ) { - access_t *p_access = (access_t*)p_input->p_access_data; - size_t i_data; + access_sys_t *p_sys = p_access->p_sys; + int i_read; + + if( p_access->info.b_eof ) + return 0; - i_data = NetRead( p_input, &p_access->socket_data, p_buffer, i_len ); + i_read = net_Read( p_access, p_sys->fd_data, NULL, p_buffer, i_len, + VLC_FALSE ); + if( i_read == 0 ) + p_access->info.b_eof = VLC_TRUE; + else if( i_read > 0 ) + p_access->info.i_pos += i_read; - return( i_data ); + return i_read; } -static int ftp_SendCommand( input_thread_t *p_input, char *psz_fmt, ... ) +/***************************************************************************** + * Control: + *****************************************************************************/ +static int Control( access_t *p_access, int i_query, va_list args ) { - access_t *p_access = (access_t*)p_input->p_access_data; - va_list args; - char *psz_buffer; -#if !defined(HAVE_VASPRINTF) || defined(SYS_DARWIN) - size_t i_size; -#endif + vlc_bool_t *pb_bool; + int *pi_int; + int64_t *pi_64; + vlc_value_t val; - va_start( args, psz_fmt ); - -#if defined(HAVE_VASPRINTF) && !defined(SYS_DARWIN) - vasprintf( &psz_buffer, psz_fmt, args ); -#else - i_size = strlen( psz_fmt ) + 2048; - psz_buffer = calloc( i_size, sizeof( char ) ); - vsnprintf( psz_buffer, i_size, psz_fmt, args ); - psz_buffer[i_size - 1] = 0; -#endif - if( !strncmp( psz_buffer, "PASS", 4 ) ) - { - msg_Dbg( p_input, "ftp_SendCommand:\"PASS xxx\"" ); - } - else - { - msg_Dbg( p_input, "ftp_SendCommand:\"%s\"", psz_buffer ); - } - psz_buffer = realloc( psz_buffer, strlen( psz_buffer ) + 3 ); - strcat( psz_buffer, "\r\n" ); - if( send( p_access->socket_cmd.i_handle, - psz_buffer, - strlen( psz_buffer ), - 0 ) == -1 ) + switch( i_query ) { - FREE( psz_buffer ); - msg_Err( p_input, "failed to send command" ); - return( -1 ); + /* */ + case ACCESS_CAN_SEEK: + pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); + *pb_bool = VLC_TRUE; + break; + case ACCESS_CAN_FASTSEEK: + pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); + *pb_bool = VLC_FALSE; + break; + case ACCESS_CAN_PAUSE: + pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); + *pb_bool = VLC_TRUE; /* FIXME */ + break; + case ACCESS_CAN_CONTROL_PACE: + pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); + *pb_bool = VLC_TRUE; /* FIXME */ + break; + + /* */ + case ACCESS_GET_MTU: + pi_int = (int*)va_arg( args, int * ); + *pi_int = 0; + break; + + case ACCESS_GET_PTS_DELAY: + pi_64 = (int64_t*)va_arg( args, int64_t * ); + var_Get( p_access, "ftp-caching", &val ); + *pi_64 = (int64_t)var_GetInteger( p_access, "ftp-caching" ) * I64C(1000); + break; + + /* */ + case ACCESS_SET_PAUSE_STATE: + /* Nothing to do */ + break; + + case ACCESS_GET_TITLE_INFO: + case ACCESS_SET_TITLE: + case ACCESS_SET_SEEKPOINT: + case ACCESS_SET_PRIVATE_ID_STATE: + return VLC_EGENERIC; + + default: + msg_Warn( p_access, "unimplemented query in control" ); + return VLC_EGENERIC; + } - FREE( psz_buffer ); + return VLC_SUCCESS; +} + +/***************************************************************************** + * ftp_*: + *****************************************************************************/ +static int ftp_SendCommand( access_t *p_access, char *psz_fmt, ... ) +{ + access_sys_t *p_sys = p_access->p_sys; + va_list args; + char *psz_cmd; + va_start( args, psz_fmt ); + vasprintf( &psz_cmd, psz_fmt, args ); va_end( args ); - return( 0 ); + msg_Dbg( p_access, "ftp_SendCommand:\"%s\"", psz_cmd); + if( net_Printf( VLC_OBJECT(p_access), p_sys->fd_cmd, NULL, "%s\r\n", + psz_cmd ) < 0 ) + { + msg_Err( p_access, "failed to send command" ); + return VLC_EGENERIC; + } + return VLC_SUCCESS; } -#define BLOCK_SIZE 1024 /* TODO support this s**t : RFC 959 allows the client to send certain TELNET strings at any moment, even in the middle of a request: @@ -521,346 +479,179 @@ static int ftp_SendCommand( input_thread_t *p_input, char *psz_fmt, ... ) These strings are not part of the requests, except in the case \377\377, where the request contains one \377. */ - -static int ftp_ReadCommand( input_thread_t *p_input, - int *pi_answer, char **ppsz_answer ) +static int ftp_ReadCommand( access_t *p_access, + int *pi_answer, char **ppsz_answer ) { - access_t *p_access = (access_t*)p_input->p_access_data; - uint8_t *p_buffer; - int i_buffer; - int i_buffer_size; - - int i_answer; - - i_buffer = 0; - i_buffer_size = BLOCK_SIZE + 1; - p_buffer = malloc( BLOCK_SIZE + 1); + access_sys_t *p_sys = p_access->p_sys; + char *psz_line; + int i_answer; - for( ;; ) + psz_line = net_Gets( p_access, p_sys->fd_cmd, NULL ); + msg_Dbg( p_access, "answer=%s", psz_line ); + if( psz_line == NULL || strlen( psz_line ) < 3 ) { - ssize_t i_read; - i_read = NetRead( p_input, &p_access->socket_cmd, - p_buffer + i_buffer, BLOCK_SIZE ); - if( i_read <= 0 || p_input->b_die || p_input->b_error ) - { - free( p_buffer ); - if( pi_answer ) *pi_answer = 500; - if( ppsz_answer ) *ppsz_answer = NULL; - return( -1 ); - } - if( i_read == 0 ) - { -// continue; - } - i_buffer += i_read; - if( i_read < BLOCK_SIZE ) - { - p_buffer[i_buffer] = '\0'; - break; - } - i_buffer_size += BLOCK_SIZE; - p_buffer = realloc( p_buffer, i_buffer_size ); + msg_Err( p_access, "cannot get answer" ); + if( psz_line ) free( psz_line ); + if( pi_answer ) *pi_answer = 500; + if( ppsz_answer ) *ppsz_answer = NULL; + return -1; } - if( i_buffer < 3 ) + if( psz_line[3] == '-' ) /* Multiple response */ { - goto exit_error; + char end[4]; + + memcpy( end, psz_line, 3 ); + end[3] = ' '; + + for( ;; ) + { + char *psz_tmp = net_Gets( p_access, p_sys->fd_cmd, NULL ); + + if( psz_tmp == NULL ) /* Error */ + break; + + if( !strncmp( psz_tmp, end, 4 ) ) + { + free( psz_tmp ); + break; + } + free( psz_tmp ); + } } - i_answer = atoi( p_buffer ); + i_answer = atoi( psz_line ); if( pi_answer ) *pi_answer = i_answer; if( ppsz_answer ) { - *ppsz_answer = p_buffer; + *ppsz_answer = psz_line; } else { - free( p_buffer ); + free( psz_line ); } return( i_answer / 100 ); - -exit_error: - free( p_buffer ); - if( pi_answer ) *pi_answer = 500; - if( ppsz_answer ) *ppsz_answer = NULL; - return( -1 ); } -static int ftp_StartStream( input_thread_t *p_input, off_t i_start ) +static int ftp_StartStream( access_t *p_access, off_t i_start ) { - access_t *p_access = (access_t*)p_input->p_access_data; + access_sys_t *p_sys = p_access->p_sys; - char psz_ip[1000]; + char psz_ipv4[16], *psz_ip; int i_answer; char *psz_arg, *psz_parser; - int a1,a2,a3,a4; - int p1,p2; int i_port; - module_t *p_network; - network_socket_t socket_desc; - if( ftp_SendCommand( p_input, "PASV" ) < 0 ) - { - msg_Err( p_input, "cannot set passive transfert mode" ); - return( -1 ); - } - if( ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 ) + psz_ip = p_sys->sz_epsv_ip; + + if( ( ftp_SendCommand( p_access, *psz_ip ? "EPSV" : "PASV" ) < 0 ) + || ( ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 ) ) { - msg_Err( p_input, "cannot set passive transfert mode" ); - return( -1 ); + msg_Err( p_access, "cannot set passive mode" ); + return VLC_EGENERIC; } + psz_parser = strchr( psz_arg, '(' ); - if( !psz_parser || sscanf( psz_parser, "(%d,%d,%d,%d,%d,%d", &a1, &a2, &a3, &a4, &p1, &p2 ) < 6 ) + if( psz_parser == NULL ) { - FREE( psz_arg ); - msg_Err( p_input, "cannot get ip/port for passive transfert mode" ); - return( -1 ); + free( psz_arg ); + msg_Err( p_access, "cannot parse passive mode response" ); + return VLC_EGENERIC; } - FREE( psz_arg ); - sprintf( psz_ip, "%d.%d.%d.%d", a1, a2, a3, a4 ); - i_port = p1 * 256 + p2; - msg_Dbg( p_input, "ip:%s port:%d", psz_ip, i_port ); - - if( ftp_SendCommand( p_input, "TYPE I" ) < 0 ) - { - msg_Err( p_input, "cannot set binary transfert mode" ); - return( -1 ); - } - if( ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 ) + if( psz_ip != NULL ) { - msg_Err( p_input, "cannot set binary transfert mode" ); - return( -1 ); - } + char psz_fmt[7] = "(|||%u"; + psz_fmt[1] = psz_fmt[2] = psz_fmt[3] = psz_parser[1]; - - if( i_start > 0 ) - { - if( ftp_SendCommand( p_input, "REST "I64Fu, i_start ) < 0 ) - { - msg_Err( p_input, "cannot set restart point" ); - return( -1 ); - } - if( ftp_ReadCommand( p_input, &i_answer, NULL ) > 3 ) + if( sscanf( psz_parser, psz_fmt, &i_port ) < 1 ) { - msg_Err( p_input, "cannot set restart point" ); - return( -1 ); + free( psz_arg ); + msg_Err( p_access, "cannot parse passive mode response" ); + return VLC_EGENERIC; } } - - msg_Dbg( p_input, "waiting for data connection..." ); - socket_desc.i_type = NETWORK_TCP; - socket_desc.psz_server_addr = psz_ip; - socket_desc.i_server_port = i_port; - socket_desc.psz_bind_addr = ""; - socket_desc.i_bind_port = 0; - p_input->p_private = (void*)&socket_desc; - if( !( p_network = module_Need( p_input, "network", "" ) ) ) - { - msg_Err( p_input, "failed to connect with server" ); - return( -1 ); - } - module_Unneed( p_input, p_network ); - p_access->socket_data.i_handle = socket_desc.i_handle; - p_input->i_mtu = socket_desc.i_mtu; - msg_Dbg( p_input, - "connection with \"%s:%d\" successful", - psz_ip, i_port ); - - if( ftp_SendCommand( p_input, "RETR %s", p_access->url.psz_path ) < 0 ) - { - msg_Err( p_input, "cannot retreive file" ); - return( -1 ); - } - /* "1xx" message */ - if( ftp_ReadCommand( p_input, &i_answer, NULL ) > 2 ) - { - msg_Err( p_input, "cannot retreive file" ); - return( -1 ); - } - - return( 0 ); -} - -static int ftp_StopStream ( input_thread_t *p_input) -{ - access_t *p_access = (access_t*)p_input->p_access_data; - - int i_answer; - - NetClose( p_input, &p_access->socket_data ); - - if( ftp_SendCommand( p_input, "ABOR" ) < 0 ) - { - msg_Err( p_input, "cannot abord file" ); - } else { - ftp_ReadCommand( p_input, &i_answer, NULL ); - ftp_ReadCommand( p_input, &i_answer, NULL ); - } - - return( 0 ); -} - -/**************************************************************************** - * - ****************************************************************************/ -static void ftp_ParseURL( url_t *p_url, char *psz_url ) -{ - char *psz_parser; - char *psz_server_port; - - p_url->psz_private = strdup( psz_url ); - - psz_parser = p_url->psz_private; + unsigned a1, a2, a3, a4, p1, p2; - while( *psz_parser == '/' ) - { - psz_parser++; - } - p_url->psz_server_addr = psz_parser; + if( ( sscanf( psz_parser, "(%u,%u,%u,%u,%u,%u", &a1, &a2, &a3, &a4, + &p1, &p2 ) < 6 ) || ( a1 > 255 ) || ( a2 > 255 ) + || ( a3 > 255 ) || ( a4 > 255 ) || ( p1 > 255 ) || ( p2 > 255 ) ) + { + free( psz_arg ); + msg_Err( p_access, "cannot parse passive mode response" ); + return VLC_EGENERIC; + } - while( *psz_parser && - *psz_parser != ':' && *psz_parser != '/' && *psz_parser != '@' ) - { - psz_parser++; + sprintf( psz_ipv4, "%u.%u.%u.%u", a1, a2, a3, a4 ); + psz_ip = psz_ipv4; + i_port = (p1 << 8) | p2; } + free( psz_arg ); - if( *psz_parser == ':' ) - { - *psz_parser = '\0'; - psz_parser++; - psz_server_port = psz_parser; + msg_Dbg( p_access, "ip:%s port:%d", psz_ip, i_port ); - while( *psz_parser && *psz_parser != '/' ) - { - psz_parser++; - } - } - else + if( ftp_SendCommand( p_access, "TYPE I" ) < 0 || + ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 ) { - psz_server_port = ""; + msg_Err( p_access, "cannot set binary transfer mode" ); + return VLC_EGENERIC; } - if( *psz_parser == '@' ) + if( i_start > 0 ) { - char *psz_bind_port; - - *psz_parser = '\0'; - psz_parser++; - - p_url->psz_bind_addr = psz_parser; - - while( *psz_parser && *psz_parser != ':' && *psz_parser != '/' ) - { - psz_parser++; - } - - if( *psz_parser == ':' ) + if( ftp_SendCommand( p_access, "REST "I64Fu, i_start ) < 0 || + ftp_ReadCommand( p_access, &i_answer, NULL ) > 3 ) { - *psz_parser = '\0'; - psz_parser++; - psz_bind_port = psz_parser; - - while( *psz_parser && *psz_parser != '/' ) - { - psz_parser++; - } + msg_Err( p_access, "cannot set restart point" ); + return VLC_EGENERIC; } - else - { - psz_bind_port = ""; - } - if( *psz_bind_port ) - { - p_url->i_bind_port = strtol( psz_bind_port, &psz_parser, 10 ); - } - else - { - p_url->i_bind_port = 0; - } - } - else - { - p_url->psz_bind_addr = ""; - p_url->i_bind_port = 0; } - if( *psz_parser == '/' ) + msg_Dbg( p_access, "waiting for data connection..." ); + p_sys->fd_data = net_ConnectTCP( p_access, psz_ip, i_port ); + if( p_sys->fd_data < 0 ) { - *psz_parser = '\0'; - psz_parser++; - p_url->psz_path = psz_parser; + msg_Err( p_access, "failed to connect with server" ); + return VLC_EGENERIC; } + msg_Dbg( p_access, "connection with \"%s:%d\" successful", + psz_ip, i_port ); - if( *psz_server_port ) - { - p_url->i_server_port = strtol( psz_server_port, &psz_parser, 10 ); - } - else + /* "1xx" message */ + if( ftp_SendCommand( p_access, "RETR %s", p_sys->url.psz_path ) < 0 || + ftp_ReadCommand( p_access, &i_answer, NULL ) > 2 ) { - p_url->i_server_port = 0; + msg_Err( p_access, "cannot retreive file" ); + return VLC_EGENERIC; } + return VLC_SUCCESS; } -/***************************************************************************** - * Read: read on a file descriptor, checking b_die periodically - *****************************************************************************/ -static ssize_t NetRead( input_thread_t *p_input, - input_socket_t *p_socket, - byte_t *p_buffer, size_t i_len ) +static int ftp_StopStream ( access_t *p_access ) { -#ifdef UNDER_CE - return -1; - -#else - struct timeval timeout; - fd_set fds; - int i_ret; + access_sys_t *p_sys = p_access->p_sys; - /* Initialize file descriptor set */ - FD_ZERO( &fds ); - FD_SET( p_socket->i_handle, &fds ); - - /* We'll wait 1 second if nothing happens */ - timeout.tv_sec = 0; - timeout.tv_usec = 1000000; - - /* Find if some data is available */ - i_ret = select( p_socket->i_handle + 1, &fds, - NULL, NULL, &timeout ); + int i_answer; - if( i_ret == -1 && errno != EINTR ) + if( ftp_SendCommand( p_access, "ABOR" ) < 0 ) { - msg_Err( p_input, "network select error (%s)", strerror(errno) ); + msg_Warn( p_access, "cannot abort file" ); + if( p_sys->fd_data > 0 ) + net_Close( p_sys->fd_data ); + p_sys->fd_data = -1; + return VLC_EGENERIC; } - else if( i_ret > 0 ) + if( p_sys->fd_data > 0 ) { - ssize_t i_recv = recv( p_socket->i_handle, p_buffer, i_len, 0 ); - - if( i_recv < 0 ) - { - msg_Err( p_input, "recv failed (%s)", strerror(errno) ); - } - - return i_recv; + net_Close( p_sys->fd_data ); + p_sys->fd_data = -1; + ftp_ReadCommand( p_access, &i_answer, NULL ); } + ftp_ReadCommand( p_access, &i_answer, NULL ); - return 0; - -#endif -} - -static void NetClose( input_thread_t *p_input, input_socket_t *p_socket ) -{ -#if defined( UNDER_CE ) - CloseHandle( (HANDLE)p_socket->i_handle ); -#elif defined( WIN32 ) - closesocket( p_socket->i_handle ); -#else - close( p_socket->i_handle ); -#endif + return VLC_SUCCESS; }