X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fftp.c;h=8138995d4685180db8ab1106c5787b633b11cee6;hb=9630c00c6b310e1a824f0bce00de89f0f777484f;hp=ec99402eafd8a16f2fc04d1a79b4e222c8e388d8;hpb=1866f27c01df43f98627192eb69d015ba7ebe449;p=vlc diff --git a/modules/access/ftp.c b/modules/access/ftp.c index ec99402eaf..8138995d46 100644 --- a/modules/access/ftp.c +++ b/modules/access/ftp.c @@ -1,10 +1,12 @@ /***************************************************************************** - * ftp.c: + * ftp.c: FTP input module ***************************************************************************** - * Copyright (C) 2001-2003 VideoLAN - * $Id: ftp.c,v 1.21 2003/09/10 21:09:05 fenrir Exp $ + * Copyright (C) 2001-2006 the VideoLAN team + * Copyright © 2006 Rémi Denis-Courmont + * $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,466 +20,584 @@ * * 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 +#ifdef HAVE_CONFIG_H +# include "config.h" #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 * ); +#include -static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer, - size_t i_len ); -static void Seek ( input_thread_t *, off_t ); +#include +#include +#include -static ssize_t NetRead ( input_thread_t *, input_socket_t *, byte_t *, size_t ); -static void NetClose( input_thread_t *, input_socket_t *); +#include +#include "vlc_url.h" +#include -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 *); +#ifndef IPPORT_FTP +# define IPPORT_FTP 21u +#endif /***************************************************************************** * Module descriptor *****************************************************************************/ +static int InOpen ( vlc_object_t * ); +static void InClose( vlc_object_t * ); +static int OutOpen ( vlc_object_t * ); +static void OutClose( 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." ) + "Caching value for FTP streams. This " \ + "value should be set in milliseconds." ) +#define USER_TEXT N_("FTP user name") +#define USER_LONGTEXT N_("User name that will " \ + "be used for the connection.") +#define PASS_TEXT N_("FTP password") +#define PASS_LONGTEXT N_("Password that will be " \ + "used for the connection.") +#define ACCOUNT_TEXT N_("FTP account") +#define ACCOUNT_LONGTEXT N_("Account that will be " \ + "used for the connection.") vlc_module_begin(); + set_shortname( "FTP" ); set_description( _("FTP input") ); - set_capability( "access", 0 ); - add_category_hint( "stream", NULL, VLC_FALSE ); - add_integer( "ftp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL, - CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE ); - add_string( "ftp-user", "anonymous", NULL, "ftp user name", "ftp user name", VLC_FALSE ); - add_string( "ftp-pwd", "anonymous@dummy.org", NULL, "ftp password", "ftp password, be careful with that option...", VLC_FALSE ); - add_string( "ftp-account", "anonymous", NULL, "ftp account", "ftp account", VLC_FALSE ); + 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@example.com", 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 ); + set_callbacks( InOpen, InClose ); + + add_submodule(); + set_shortname( "FTP" ); + set_description( _("FTP upload output") ); + set_capability( "sout access", 0 ); + set_category( CAT_SOUT ); + set_subcategory( SUBCAT_SOUT_ACO ); + set_callbacks( OutOpen, OutClose ); 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 ssize_t Read( access_t *, uint8_t *, size_t ); +static ssize_t Write( sout_access_out_t *, block_t * ); +static int Seek( access_t *, int64_t ); +static int OutSeek( sout_access_out_t *, off_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; -} access_t; + char sz_epsv_ip[NI_MAXNUMERICHOST]; +}; +#define GET_OUT_SYS( p_this ) \ + ((access_sys_t *)(((sout_access_out_t *)(p_this))->p_sys)) +static int ftp_SendCommand( vlc_object_t *, access_sys_t *, const char *, ... ); +static int ftp_ReadCommand( vlc_object_t *, access_sys_t *, int *, char ** ); +static int ftp_StartStream( vlc_object_t *, access_sys_t *, int64_t ); +static int ftp_StopStream ( vlc_object_t *, access_sys_t * ); -/**************************************************************************** - **************************************************************************** - ******************* ******************* - ******************* Main functions ******************* - ******************* ******************* - **************************************************************************** - ****************************************************************************/ - -/**************************************************************************** - * Open: connect to ftp server and ask for file - ****************************************************************************/ -static int Open( vlc_object_t *p_this ) +static int Login( vlc_object_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; - - /* *** 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 ); + int i_answer; + char *psz; - 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 *** */ + int fd = p_sys->fd_cmd = net_ConnectTCP( p_access, p_sys->url.psz_host, + p_sys->url.i_port ); + if( fd == -1 ) { - p_url->i_server_port = 21; /* default port */ + msg_Err( p_access, "connection failed" ); + intf_UserFatal( p_access, VLC_FALSE, _("Network interaction failed"), + _("VLC could not connect with the given 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"; - } + while( ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) == 1 ); - /* 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; - socket_desc.i_ttl = 0; - p_input->p_private = (void*)&socket_desc; - if( !( p_network = module_Need( p_input, "network", psz_network ) ) ) + if( i_answer / 100 != 2 ) { - msg_Err( p_input, "failed to connect with server" ); - FREE( p_access->url.psz_private ); - FREE( p_input->p_access_data ); - return( -1 ); + msg_Err( p_access, "connection rejected" ); + intf_UserFatal( p_access, VLC_FALSE, _("Network interaction failed"), + _("VLC's connection to the given server was rejected.") ); + 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 ); + msg_Dbg( p_access, "connection accepted (%d)", i_answer ); - 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 ) - { - break; - } - } - - if( i_answer / 100 != 2 ) - { - msg_Err( p_input, "connection rejected" ); - goto exit_error; - } + if( p_sys->url.psz_username && *p_sys->url.psz_username ) + psz = strdup( p_sys->url.psz_username ); else - { - msg_Dbg( p_input, "connection accepted (%d)", i_answer ); - } + psz = var_CreateGetString( p_access, "ftp-user" ); - psz_user = config_GetPsz( p_input, "ftp-user" ); - if( ftp_SendCommand( p_input, "USER %s", psz_user ) < 0 ) + if( ftp_SendCommand( p_access, p_sys, "USER %s", psz ) < 0 || + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 ) { - FREE( psz_user ); - goto exit_error; + free( psz ); + return -1; } - FREE( psz_user ); + free( psz ); - if( ftp_ReadCommand( p_input, &i_answer, NULL ) < 0) - { - msg_Err( p_input, "failed to get answer" ); - goto exit_error; - } 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 ) + msg_Dbg( p_access, "password needed" ); + if( p_sys->url.psz_password && *p_sys->url.psz_password ) + psz = strdup( p_sys->url.psz_password ); + else + psz = var_CreateGetString( p_access, "ftp-pwd" ); + + if( ftp_SendCommand( p_access, p_sys, "PASS %s", psz ) < 0 || + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 ) { - FREE( psz_pwd ); - goto exit_error; - } - FREE( psz_pwd ); - if( ftp_ReadCommand( p_input, &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 ) + msg_Dbg( p_access, "account needed" ); + psz = var_CreateGetString( p_access, "ftp-account" ); + if( ftp_SendCommand( p_access, p_sys, "ACCT %s", + psz ) < 0 || + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 ) { - FREE( psz_account ); - goto exit_error; - } - FREE( psz_account ); - if( ftp_ReadCommand( p_input, &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" ); + intf_UserFatal( p_access, VLC_FALSE, + _("Network interaction failed"), + _("Your account was 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" ); + intf_UserFatal( p_access, VLC_FALSE, + _("Network interaction failed"), + _("Your password was rejected.") ); + return -1; } break; default: - msg_Err( p_input, "user rejected" ); - goto exit_error; + msg_Err( p_access, "user rejected" ); + intf_UserFatal( p_access, VLC_FALSE, + _("Network interaction failed"), + _("Your connection attempt to the server was rejected.") ); + return -1; } - if( ftp_SendCommand( p_input, "TYPE I" ) < 0 ) + return 0; +} + +static int Connect( vlc_object_t *p_access, access_sys_t *p_sys ) +{ + if( Login( p_access, p_sys ) < 0 ) + return -1; + + /* Extended passive mode */ + if( ftp_SendCommand( p_access, p_sys, "EPSV ALL" ) < 0 ) { - msg_Err( p_input, "cannot set binary transfert mode" ); - goto exit_error; + msg_Err( p_access, "cannot request extended passive mode" ); + net_Close( p_sys->fd_cmd ); + return -1; } - if( ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 ) + + if( ftp_ReadCommand( p_access, p_sys, NULL, NULL ) == 2 ) { - msg_Err( p_input, "cannot set binary transfert mode" ); - goto exit_error; + if( net_GetPeerAddress( p_sys->fd_cmd, p_sys->sz_epsv_ip, NULL ) ) + { + net_Close( p_sys->fd_cmd ); + return -1; + } + } + 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. + */ + msg_Info( p_access, "FTP Extended passive mode disabled" ); + net_Close( p_sys->fd_cmd ); + + if( Login( p_access, p_sys ) ) + { + net_Close( p_sys->fd_cmd ); + return -1; + } + } + + /* check binary mode support */ + if( ftp_SendCommand( p_access, p_sys, "TYPE I" ) < 0 || + ftp_ReadCommand( p_access, p_sys, NULL, NULL ) != 2 ) + { + msg_Err( p_access, "cannot set binary transfer mode" ); + net_Close( p_sys->fd_cmd ); + return -1; } + return 0; +} + + +static int parseURL( vlc_url_t *url, const char *path ) +{ + if( path == NULL ) + return -1; + + /* *** Parse URL and get server addr/port and path *** */ + while( *path == '/' ) + path++; + + vlc_UrlParse( url, path, 0 ); + + if( url->psz_host == NULL || *url->psz_host == '\0' ) + return -1; + + if( url->i_port <= 0 ) + url->i_port = IPPORT_FTP; /* default port */ + + /* FTP URLs are relative to user's default directory (RFC1738) + For absolute path use ftp://foo.bar//usr/local/etc/filename */ + + if( *url->psz_path == '/' ) + url->psz_path++; + + return 0; +} + + +/**************************************************************************** + * Open: connect to ftp server and ask for file + ****************************************************************************/ +static int InOpen( vlc_object_t *p_this ) +{ + access_t *p_access = (access_t*)p_this; + access_sys_t *p_sys; + char *psz_arg; + + /* Init p_access */ + STANDARD_READ_ACCESS_INIT + p_sys->fd_data = -1; + + if( parseURL( &p_sys->url, p_access->psz_path ) ) + goto exit_error; + + if( Connect( p_this, p_sys ) ) + goto exit_error; + /* get size */ - if( ftp_SendCommand( p_input, "SIZE %s", p_url->psz_path ) < 0 ) + if( ftp_SendCommand( p_this, p_sys, "SIZE %s", p_sys->url.psz_path ) < 0 || + ftp_ReadCommand( p_this, p_sys, NULL, &psz_arg ) != 2 ) { - msg_Err( p_input, "cannot get file size" ); + msg_Err( p_access, "cannot get file size" ); + net_Close( p_sys->fd_cmd ); goto exit_error; } - if( ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 ) + p_access->info.i_size = atoll( &psz_arg[4] ); + free( psz_arg ); + msg_Dbg( p_access, "file size: "I64Fd, p_access->info.i_size ); + + /* Start the 'stream' */ + if( ftp_StartStream( p_this, p_sys, 0 ) < 0 ) { - msg_Err( p_input, "cannot get file size" ); + msg_Err( p_access, "cannot retrieve file" ); + net_Close( p_sys->fd_cmd ); goto exit_error; } - p_access->i_filesize = atoll( psz_arg + 4 ); + /* Update default_pts to a suitable value for ftp access */ + var_Create( p_access, "ftp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + + return VLC_SUCCESS; + +exit_error: + vlc_UrlClean( &p_sys->url ); + free( p_sys ); + return VLC_EGENERIC; +} + +static int OutOpen( vlc_object_t *p_this ) +{ + sout_access_out_t *p_access = (sout_access_out_t *)p_this; + access_sys_t *p_sys; + + p_sys = malloc( sizeof( *p_sys ) ); + if( p_sys == NULL ) + return VLC_ENOMEM; + memset( p_sys, 0, sizeof( *p_sys ) ); + + /* Init p_access */ + p_sys->fd_data = -1; + + if( parseURL( &p_sys->url, p_access->psz_path ) ) + goto exit_error; - msg_Dbg( p_input, "file size: "I64Fd, p_access->i_filesize ); - FREE( psz_arg ); + if( Connect( p_this, p_sys ) ) + goto exit_error; - if( ftp_StartStream( p_input, 0 ) < 0 ) + /* Start the 'stream' */ + if( ftp_StartStream( p_this, p_sys, 0 ) < 0 ) { - msg_Err( p_input, "cannot retrieve file" ); + msg_Err( p_access, "cannot store file" ); + net_Close( p_sys->fd_cmd ); goto exit_error; } - /* *** set exported functions *** */ - p_input->pf_read = Read; - p_input->pf_seek = Seek; - p_input->pf_set_program = input_SetProgram; - p_input->pf_set_area = NULL; - - p_input->p_private = NULL; - - /* *** 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.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 ); - /* Update default_pts to a suitable value for ftp access */ - p_input->i_pts_delay = config_GetInt( p_input, "ftp-caching" ) * 1000; + p_access->pf_seek = OutSeek; + p_access->pf_write = Write; + p_access->p_sys = (void *)p_sys; - 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 ); + vlc_UrlClean( &p_sys->url ); + free( p_sys ); + return VLC_EGENERIC; } /***************************************************************************** * Close: free unused data structures *****************************************************************************/ -static void Close( vlc_object_t *p_this ) +static void Close( vlc_object_t *p_access, access_sys_t *p_sys ) { - input_thread_t *p_input = (input_thread_t *)p_this; - access_t *p_access = (access_t*)p_input->p_access_data; + msg_Dbg( p_access, "stopping stream" ); + ftp_StopStream( p_access, p_sys ); - msg_Dbg( p_input, "stopping stream" ); - ftp_StopStream( p_input ); - - if( ftp_SendCommand( p_input, "QUIT" ) < 0 ) + if( ftp_SendCommand( p_access, p_sys, "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, p_sys, NULL, NULL ); } + net_Close( p_sys->fd_cmd ); + /* free memory */ + vlc_UrlClean( &p_sys->url ); + free( p_sys ); +} - NetClose( p_input, &p_access->socket_cmd ); +static void InClose( vlc_object_t *p_this ) +{ + Close( p_this, ((access_t *)p_this)->p_sys); +} - /* free memory */ - FREE( p_access->url.psz_private ); +static void OutClose( vlc_object_t *p_this ) +{ + Close( p_this, GET_OUT_SYS(p_this)); } + /***************************************************************************** * Seek: try to go at the right place *****************************************************************************/ -static void Seek( input_thread_t * p_input, off_t i_pos ) +static int _Seek( vlc_object_t *p_access, access_sys_t *p_sys, int64_t i_pos ) { - //access_t *p_access = (access_t*)p_input->p_access_data; if( i_pos < 0 ) - { - return; - } - vlc_mutex_lock( &p_input->stream.stream_lock ); + return VLC_EGENERIC; - msg_Dbg( p_input, "seeking to "I64Fd, i_pos ); + msg_Dbg( p_access, "seeking to "I64Fd, i_pos ); - ftp_StopStream( p_input ); - ftp_StartStream( p_input, i_pos ); + ftp_StopStream( (vlc_object_t *)p_access, p_sys ); + if( ftp_StartStream( (vlc_object_t *)p_access, p_sys, i_pos ) < 0 ) + return VLC_EGENERIC; - p_input->stream.p_selected_area->i_tell = i_pos; - vlc_mutex_unlock( &p_input->stream.stream_lock ); + return VLC_SUCCESS; } -static ssize_t Read ( input_thread_t * p_input, byte_t * p_buffer, - size_t i_len ) +static int Seek( access_t *p_access, int64_t i_pos ) { - access_t *p_access = (access_t*)p_input->p_access_data; - size_t i_data; + int val = _Seek( (vlc_object_t *)p_access, p_access->p_sys, i_pos ); + if( val ) + return val; - i_data = NetRead( p_input, &p_access->socket_data, p_buffer, i_len ); + p_access->info.b_eof = VLC_FALSE; + p_access->info.i_pos = i_pos; - return( i_data ); + return VLC_SUCCESS; } -static int ftp_SendCommand( input_thread_t *p_input, char *psz_fmt, ... ) +static int OutSeek( sout_access_out_t *p_access, off_t i_pos ) { - access_t *p_access = (access_t*)p_input->p_access_data; - va_list args; - char *psz_buffer; -#if !defined(HAVE_VASPRINTF) || defined(SYS_DARWIN) || defined(SYS_BEOS) - size_t i_size; -#endif + return _Seek( (vlc_object_t *)p_access, GET_OUT_SYS( p_access ), i_pos); +} - va_start( args, psz_fmt ); +/***************************************************************************** + * Read: + *****************************************************************************/ +static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len ) +{ + access_sys_t *p_sys = p_access->p_sys; + int i_read; -#if defined(HAVE_VASPRINTF) && !defined(SYS_DARWIN) && !defined(SYS_BEOS) - 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 + assert( p_sys->fd_data != -1 ); + assert( p_access->i_object_type == VLC_OBJECT_ACCESS ); + + if( p_access->info.b_eof ) + return 0; + + 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_read; +} + +/***************************************************************************** + * Write: + *****************************************************************************/ +static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer ) +{ + access_sys_t *p_sys = GET_OUT_SYS(p_access); + size_t i_write = 0; + + assert( p_sys->fd_data != -1 ); + + while( p_buffer != NULL ) { - msg_Dbg( p_input, "ftp_SendCommand:\"%s\"", psz_buffer ); + block_t *p_next = p_buffer->p_next;; + + i_write += net_Write( p_access, p_sys->fd_data, NULL, + p_buffer->p_buffer, p_buffer->i_buffer ); + block_Release( p_buffer ); + + p_buffer = p_next; } - 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 ) + + return i_write; +} + +/***************************************************************************** + * Control: + *****************************************************************************/ +static int Control( access_t *p_access, int i_query, va_list args ) +{ + vlc_bool_t *pb_bool; + int *pi_int; + int64_t *pi_64; + vlc_value_t val; + + 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: + pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); + if ( !pb_bool ) + return Seek( p_access, p_access->info.i_pos ); + break; + + case ACCESS_GET_TITLE_INFO: + case ACCESS_SET_TITLE: + case ACCESS_SET_SEEKPOINT: + case ACCESS_SET_PRIVATE_ID_STATE: + case ACCESS_GET_CONTENT_TYPE: + case ACCESS_GET_META: + return VLC_EGENERIC; + + default: + msg_Warn( p_access, "unimplemented query in control: %d", i_query); + return VLC_EGENERIC; + } - FREE( psz_buffer ); + return VLC_SUCCESS; +} + +/***************************************************************************** + * ftp_*: + *****************************************************************************/ +static int ftp_SendCommand( vlc_object_t *p_access, access_sys_t *p_sys, + const char *psz_fmt, ... ) +{ + 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: @@ -493,357 +613,185 @@ 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( vlc_object_t *p_access, access_sys_t *p_sys, + 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; + char *psz_line; + int i_answer; - i_buffer = 0; - i_buffer_size = BLOCK_SIZE + 1; - p_buffer = malloc( BLOCK_SIZE + 1); - - for( ;; ) + psz_line = net_Gets( p_access, p_sys->fd_cmd, NULL ); + 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" ); + free( psz_line ); + if( pi_answer ) *pi_answer = 500; + if( ppsz_answer ) *ppsz_answer = NULL; + return -1; } + msg_Dbg( p_access, "answer=%s", psz_line ); - 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( vlc_object_t *p_access, access_sys_t *p_sys, + int64_t i_start ) { - access_t *p_access = (access_t*)p_input->p_access_data; - - char psz_ip[1000]; + char psz_ipv4[16], *psz_ip = p_sys->sz_epsv_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 ) + assert( p_sys->fd_data == -1 ); + + if( ( ftp_SendCommand( p_access, p_sys, *psz_ip ? "EPSV" : "PASV" ) < 0 ) + || ( ftp_ReadCommand( p_access, p_sys, &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 ) { - 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 ) + 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; } - if( ftp_ReadCommand( p_input, &i_answer, NULL ) > 3 ) - { - msg_Err( p_input, "cannot set restart point" ); - return( -1 ); - } - } - - 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; - socket_desc.i_ttl = 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 ); -} + unsigned a1, a2, a3, a4, p1, p2; -/**************************************************************************** - * - ****************************************************************************/ -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; - - 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, p_sys, "TYPE I" ) < 0 || + ftp_ReadCommand( p_access, p_sys, &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 == ':' ) - { - *psz_parser = '\0'; - psz_parser++; - psz_bind_port = psz_parser; - - while( *psz_parser && *psz_parser != '/' ) - { - psz_parser++; - } - } - else - { - psz_bind_port = ""; - } - if( *psz_bind_port ) - { - p_url->i_bind_port = strtol( psz_bind_port, &psz_parser, 10 ); - } - else + if( ftp_SendCommand( p_access, p_sys, "REST "I64Fu, i_start ) < 0 || + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) > 3 ) { - p_url->i_bind_port = 0; + msg_Err( p_access, "cannot set restart offset" ); + return VLC_EGENERIC; } } - 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, p_sys, "%s %s", + (p_access->i_object_type == VLC_OBJECT_ACCESS) + ? "RETR" : "STOR", + p_sys->url.psz_path ) < 0 || + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) > 2 ) { - p_url->i_server_port = 0; + msg_Err( p_access, "cannot retrieve file" ); + return VLC_EGENERIC; } + + shutdown( p_sys->fd_data, + ( p_access->i_object_type == VLC_OBJECT_ACCESS ) ); + + 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 ( vlc_object_t *p_access, access_sys_t *p_sys ) { -#ifdef UNDER_CE - return -1; - -#else - struct timeval timeout; - fd_set fds; - ssize_t i_recv; - int i_ret; - - /* 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 = 1; - timeout.tv_usec = 0; - - /* Find if some data is available */ - while( (i_ret = select( p_socket->i_handle + 1, &fds, - NULL, NULL, &timeout )) == 0 - || (i_ret < 0 && errno == EINTR) ) + if( ftp_SendCommand( p_access, p_sys, "ABOR" ) < 0 ) { - FD_ZERO( &fds ); - FD_SET( p_socket->i_handle, &fds ); - timeout.tv_sec = 1; - timeout.tv_usec = 0; - - if( p_input->b_die || p_input->b_error ) - { - return 0; - } + 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; } - if( i_ret < 0 ) + if( p_sys->fd_data != -1 ) { - msg_Err( p_input, "network select error (%s)", strerror(errno) ); - return -1; - } + int i_answer; + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ); + if ( i_answer != 227 ) + /* If answer is from the previous command, + * rathen that succesful ABOR - read next command */ + ftp_ReadCommand( p_access, p_sys, NULL, NULL ); - 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) ); + net_Close( p_sys->fd_data ); + p_sys->fd_data = -1; } - return i_recv; - -#endif + return VLC_SUCCESS; } - -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 -} -