X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess%2Fftp.c;h=7d874566ef85f3d797740efdd6babb455a3c7f71;hb=5ebacd11d10f9acc39afc0b3d41beb280eaac293;hp=6890418772ffe8031c7310ec507cdfb413e879b1;hpb=3b6a46b8c57fa04cf87d17777cb33f2bd83a448b;p=vlc diff --git a/modules/access/ftp.c b/modules/access/ftp.c index 6890418772..7d874566ef 100644 --- a/modules/access/ftp.c +++ b/modules/access/ftp.c @@ -1,10 +1,12 @@ /***************************************************************************** * ftp.c: FTP input module ***************************************************************************** - * Copyright (C) 2001-2004 VideoLAN + * 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,151 +20,145 @@ * * 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 +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -#include -#include +#include +#include -#include "network.h" +#include + +#include +#include + +#include +#include "vlc_url.h" +#include + +#ifndef IPPORT_FTP +# define IPPORT_FTP 21u +#endif /***************************************************************************** * Module descriptor *****************************************************************************/ -static int Open ( vlc_object_t * ); -static void Close( vlc_object_t * ); +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 millisecond units." ) + "Caching value for FTP streams. This " \ + "value should be set in milliseconds." ) #define USER_TEXT N_("FTP user name") -#define USER_LONGTEXT N_("Allows you to modify the user name that will " \ +#define USER_LONGTEXT N_("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 " \ +#define PASS_LONGTEXT N_("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 " \ +#define ACCOUNT_LONGTEXT N_("Account that will be " \ "used for the connection.") vlc_module_begin(); - set_description( _("FTP input") ); - set_capability( "access2", 0 ); + set_shortname( "FTP" ); + set_description( N_("FTP input") ); + set_capability( "access", 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 ); + CACHING_TEXT, CACHING_LONGTEXT, 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 ); + false ); + add_string( "ftp-pwd", "anonymous@example.com", NULL, PASS_TEXT, + PASS_LONGTEXT, false ); add_string( "ftp-account", "anonymous", NULL, ACCOUNT_TEXT, - ACCOUNT_LONGTEXT, VLC_FALSE ); + ACCOUNT_LONGTEXT, false ); add_shortcut( "ftp" ); - set_callbacks( Open, Close ); + set_callbacks( InOpen, InClose ); + + add_submodule(); + set_shortname( "FTP" ); + set_description( N_("FTP upload output") ); + set_capability( "sout access", 0 ); + set_category( CAT_SOUT ); + set_subcategory( SUBCAT_SOUT_ACO ); + set_callbacks( OutOpen, OutClose ); vlc_module_end(); /***************************************************************************** * Local prototypes *****************************************************************************/ -static int Read( access_t *, uint8_t *, int ); +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 ); struct access_sys_t { - vlc_url_t url; + vlc_url_t url; + + int fd_cmd; + int fd_data; - int fd_cmd; - int fd_data; + char sz_epsv_ip[NI_MAXNUMERICHOST]; + bool out; }; +#define GET_OUT_SYS( p_this ) \ + ((access_sys_t *)(((sout_access_out_t *)(p_this))->p_sys)) -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 *); +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 * ); -/**************************************************************************** - * 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 ) { - 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 == '/' ) - { - psz++; - } - vlc_UrlParse( &p_sys->url, psz, 0 ); - - if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' ) - { - msg_Err( p_access, "invalid server name" ); - goto exit_error; - } - if( p_sys->url.i_port <= 0 ) - { - p_sys->url.i_port = 21; /* default port */ - } + int i_answer; + char *psz; /* *** Open a TCP connection with server *** */ - msg_Dbg( p_access, "waiting for connection..." ); - p_sys->fd_cmd = net_OpenTCP( p_access, p_sys->url.psz_host, - p_sys->url.i_port ); - if( p_sys->fd_cmd < 0 ) + int fd = p_sys->fd_cmd = net_ConnectTCP( p_access, p_sys->url.psz_host, + p_sys->url.i_port ); + if( fd == -1 ) { - msg_Err( p_access, "failed to connect with server" ); - goto exit_error; + msg_Err( p_access, "connection failed" ); + intf_UserFatal( p_access, false, _("Network interaction failed"), + _("VLC could not connect with the given server.") ); + return -1; } - for( ;; ) - { - if( ftp_ReadCommand( p_access, &i_answer, NULL ) != 1 ) - { - break; - } - } + while( ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) == 1 ); + if( i_answer / 100 != 2 ) { msg_Err( p_access, "connection rejected" ); - goto exit_error; + intf_UserFatal( p_access, false, _("Network interaction failed"), + _("VLC's connection to the given server was rejected.") ); + return -1; } msg_Dbg( p_access, "connection accepted (%d)", i_answer ); - psz = var_CreateGetString( p_access, "ftp-user" ); - if( ftp_SendCommand( p_access, "USER %s", psz ) < 0 || - ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 ) + if( p_sys->url.psz_username && *p_sys->url.psz_username ) + psz = strdup( p_sys->url.psz_username ); + else + psz = var_CreateGetString( p_access, "ftp-user" ); + + if( ftp_SendCommand( p_access, p_sys, "USER %s", psz ) < 0 || + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 ) { free( psz ); - goto exit_error; + return -1; } free( psz ); @@ -173,12 +169,16 @@ static int Open( vlc_object_t *p_this ) break; case 3: 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 ) + 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 ); - goto exit_error; + return -1; } free( psz ); @@ -190,56 +190,160 @@ static int Open( vlc_object_t *p_this ) case 3: msg_Dbg( p_access, "account needed" ); psz = var_CreateGetString( p_access, "ftp-account" ); - if( ftp_SendCommand( p_access, "ACCT %s", + if( ftp_SendCommand( p_access, p_sys, "ACCT %s", psz ) < 0 || - ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 ) + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 ) { free( psz ); - goto exit_error; + return -1; } free( psz ); if( i_answer / 100 != 2 ) { msg_Err( p_access, "account rejected" ); - goto exit_error; + intf_UserFatal( p_access, false, + _("Network interaction failed"), + _("Your account was rejected.") ); + return -1; } msg_Dbg( p_access, "account accepted" ); break; default: msg_Err( p_access, "password rejected" ); - goto exit_error; + intf_UserFatal( p_access, false, + _("Network interaction failed"), + _("Your password was rejected.") ); + return -1; } break; default: msg_Err( p_access, "user rejected" ); - goto exit_error; + intf_UserFatal( p_access, false, + _("Network interaction failed"), + _("Your connection attempt to the server was rejected.") ); + return -1; } - /* binary mode */ - if( ftp_SendCommand( p_access, "TYPE I" ) < 0 || - ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 ) + 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_access, "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_access, p_sys, NULL, NULL ) == 2 ) + { + 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; + p_sys->out = false; + + 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_access, "SIZE %s", p_sys->url.psz_path ) < 0 || - ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 ) + 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_access, "cannot get file size" ); + net_Close( p_sys->fd_cmd ); 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 ); + msg_Dbg( p_access, "file size: %"PRId64, p_access->info.i_size ); /* Start the 'stream' */ - if( ftp_StartStream( p_access, 0 ) < 0 ) + if( ftp_StartStream( p_this, p_sys, 0 ) < 0 ) { msg_Err( p_access, "cannot retrieve file" ); + net_Close( p_sys->fd_cmd ); goto exit_error; } @@ -249,10 +353,46 @@ static int Open( vlc_object_t *p_this ) return VLC_SUCCESS; exit_error: - if( p_sys->fd_cmd > 0 ) + 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; + p_sys->out = true; + + if( parseURL( &p_sys->url, p_access->psz_path ) ) + goto exit_error; + + if( Connect( p_this, p_sys ) ) + goto exit_error; + + /* Start the 'stream' */ + if( ftp_StartStream( p_this, p_sys, 0 ) < 0 ) { + msg_Err( p_access, "cannot store file" ); net_Close( p_sys->fd_cmd ); + goto exit_error; } + + p_access->pf_seek = OutSeek; + p_access->pf_write = Write; + p_access->p_sys = (void *)p_sys; + + return VLC_SUCCESS; + +exit_error: vlc_UrlClean( &p_sys->url ); free( p_sys ); return VLC_EGENERIC; @@ -261,21 +401,18 @@ exit_error: /***************************************************************************** * 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 ) { - access_t *p_access = (access_t*)p_this; - access_sys_t *p_sys = p_access->p_sys; - msg_Dbg( p_access, "stopping stream" ); - ftp_StopStream( p_access ); + ftp_StopStream( p_access, p_sys ); - if( ftp_SendCommand( p_access, "QUIT" ) < 0 ) + if( ftp_SendCommand( p_access, p_sys, "QUIT" ) < 0 ) { msg_Warn( p_access, "cannot quit" ); } else { - ftp_ReadCommand( p_access, NULL, NULL ); + ftp_ReadCommand( p_access, p_sys, NULL, NULL ); } net_Close( p_sys->fd_cmd ); @@ -284,56 +421,105 @@ static void Close( vlc_object_t *p_this ) free( p_sys ); } +static void InClose( vlc_object_t *p_this ) +{ + Close( p_this, ((access_t *)p_this)->p_sys); +} + +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 int Seek( access_t *p_access, int64_t i_pos ) +static int _Seek( vlc_object_t *p_access, access_sys_t *p_sys, int64_t i_pos ) { if( i_pos < 0 ) - { return VLC_EGENERIC; - } - msg_Dbg( p_access, "seeking to "I64Fd, i_pos ); - ftp_StopStream( p_access ); - if( ftp_StartStream( p_access, i_pos ) < 0 ) - { - p_access->info.b_eof = VLC_TRUE; + msg_Dbg( p_access, "seeking to %"PRId64, 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_access->info.b_eof = VLC_FALSE; + return VLC_SUCCESS; +} + +static int Seek( access_t *p_access, int64_t i_pos ) +{ + int val = _Seek( (vlc_object_t *)p_access, p_access->p_sys, i_pos ); + if( val ) + return val; + + p_access->info.b_eof = false; p_access->info.i_pos = i_pos; return VLC_SUCCESS; } +static int OutSeek( sout_access_out_t *p_access, off_t i_pos ) +{ + return _Seek( (vlc_object_t *)p_access, GET_OUT_SYS( p_access ), i_pos); +} + /***************************************************************************** * Read: *****************************************************************************/ -static int Read( access_t *p_access, uint8_t *p_buffer, int i_len ) +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; + assert( p_sys->fd_data != -1 ); + assert( !p_sys->out ); + if( p_access->info.b_eof ) return 0; - i_read = net_Read( p_access, p_sys->fd_data, p_buffer, i_len, VLC_FALSE ); + i_read = net_Read( p_access, p_sys->fd_data, NULL, p_buffer, i_len, + false ); if( i_read == 0 ) - p_access->info.b_eof = VLC_TRUE; + p_access->info.b_eof = 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 ) + { + 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; + } + + return i_write; +} + /***************************************************************************** * Control: *****************************************************************************/ static int Control( access_t *p_access, int i_query, va_list args ) { - vlc_bool_t *pb_bool; + bool *pb_bool; int *pi_int; int64_t *pi_64; vlc_value_t val; @@ -342,20 +528,20 @@ static int Control( access_t *p_access, int i_query, va_list args ) { /* */ case ACCESS_CAN_SEEK: - pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); - *pb_bool = VLC_TRUE; + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = true; break; case ACCESS_CAN_FASTSEEK: - pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); - *pb_bool = VLC_FALSE; + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = false; break; case ACCESS_CAN_PAUSE: - pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); - *pb_bool = VLC_TRUE; /* FIXME */ + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = true; /* FIXME */ break; case ACCESS_CAN_CONTROL_PACE: - pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); - *pb_bool = VLC_TRUE; /* FIXME */ + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = true; /* FIXME */ break; /* */ @@ -367,21 +553,26 @@ static int Control( access_t *p_access, int i_query, va_list args ) 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); + *pi_64 = (int64_t)var_GetInteger( p_access, "ftp-caching" ) * INT64_C(1000); break; /* */ case ACCESS_SET_PAUSE_STATE: - /* Nothing to do */ + pb_bool = (bool*)va_arg( args, bool* ); + 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_Err( p_access, "unimplemented query in control" ); + msg_Warn( p_access, "unimplemented query in control: %d", i_query); return VLC_EGENERIC; } @@ -391,25 +582,19 @@ static int Control( access_t *p_access, int i_query, va_list args ) /***************************************************************************** * ftp_*: *****************************************************************************/ -static int ftp_SendCommand( access_t *p_access, char *psz_fmt, ... ) +static int ftp_SendCommand( vlc_object_t *p_access, access_sys_t *p_sys, + const char *psz_fmt, ... ) { - access_sys_t *p_sys = p_access->p_sys; va_list args; char *psz_cmd; - int i_ret; va_start( args, psz_fmt ); vasprintf( &psz_cmd, psz_fmt, args ); va_end( args ); msg_Dbg( p_access, "ftp_SendCommand:\"%s\"", psz_cmd); - if( ( i_ret = net_Printf( VLC_OBJECT(p_access), p_sys->fd_cmd, - "%s", psz_cmd ) ) > 0 ) - { - i_ret = net_Printf( VLC_OBJECT(p_access), p_sys->fd_cmd, "\n" ); - } - - if( i_ret < 0 ) + 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; @@ -432,23 +617,45 @@ static int ftp_SendCommand( access_t *p_access, 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( access_t *p_access, +static int ftp_ReadCommand( vlc_object_t *p_access, access_sys_t *p_sys, int *pi_answer, char **ppsz_answer ) { - access_sys_t *p_sys = p_access->p_sys; char *psz_line; int i_answer; - psz_line = net_Gets( p_access, p_sys->fd_cmd ); - msg_Dbg( p_access, "answer=%s", psz_line ); + psz_line = net_Gets( p_access, p_sys->fd_cmd, NULL ); if( psz_line == NULL || strlen( psz_line ) < 3 ) { msg_Err( p_access, "cannot get answer" ); - if( psz_line ) free( psz_line ); + 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( psz_line[3] == '-' ) /* Multiple response */ + { + 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( psz_line ); @@ -464,58 +671,83 @@ static int ftp_ReadCommand( access_t *p_access, return( i_answer / 100 ); } -static int ftp_StartStream( access_t *p_access, off_t i_start ) +static int ftp_StartStream( vlc_object_t *p_access, access_sys_t *p_sys, + int64_t i_start ) { - access_sys_t *p_sys = p_access->p_sys; - - 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; - if( ftp_SendCommand( p_access, "PASV" ) < 0 || - ftp_ReadCommand( p_access, &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_access, "cannot set passive transfert mode" ); + 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_access, "cannot get ip/port for passive transfert mode" ); + msg_Err( p_access, "cannot parse passive mode response" ); return VLC_EGENERIC; } + + if( *psz_ip ) + { + char psz_fmt[7] = "(|||%u"; + psz_fmt[1] = psz_fmt[2] = psz_fmt[3] = psz_parser[1]; + + if( sscanf( psz_parser, psz_fmt, &i_port ) < 1 ) + { + free( psz_arg ); + msg_Err( p_access, "cannot parse passive mode response" ); + return VLC_EGENERIC; + } + } + else + { + unsigned a1, a2, a3, a4, p1, p2; + + 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; + } + + sprintf( psz_ipv4, "%u.%u.%u.%u", a1, a2, a3, a4 ); + psz_ip = psz_ipv4; + i_port = (p1 << 8) | p2; + } free( psz_arg ); - sprintf( psz_ip, "%d.%d.%d.%d", a1, a2, a3, a4 ); - i_port = p1 * 256 + p2; msg_Dbg( p_access, "ip:%s port:%d", psz_ip, i_port ); - if( ftp_SendCommand( p_access, "TYPE I" ) < 0 || - ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 ) + if( ftp_SendCommand( p_access, p_sys, "TYPE I" ) < 0 || + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) != 2 ) { - msg_Err( p_access, "cannot set binary transfert mode" ); + msg_Err( p_access, "cannot set binary transfer mode" ); return VLC_EGENERIC; } if( i_start > 0 ) { - if( ftp_SendCommand( p_access, "REST "I64Fu, i_start ) < 0 || - ftp_ReadCommand( p_access, &i_answer, NULL ) > 3 ) + if( ftp_SendCommand( p_access, p_sys, "REST %"PRIu64, i_start ) < 0 || + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) > 3 ) { - msg_Err( p_access, "cannot set restart point" ); + msg_Err( p_access, "cannot set restart offset" ); return VLC_EGENERIC; } } msg_Dbg( p_access, "waiting for data connection..." ); - p_sys->fd_data = net_OpenTCP( p_access, psz_ip, i_port ); + p_sys->fd_data = net_ConnectTCP( p_access, psz_ip, i_port ); if( p_sys->fd_data < 0 ) { msg_Err( p_access, "failed to connect with server" ); @@ -525,37 +757,43 @@ static int ftp_StartStream( access_t *p_access, off_t i_start ) psz_ip, i_port ); /* "1xx" message */ - if( ftp_SendCommand( p_access, "RETR %s", p_sys->url.psz_path ) < 0 || - ftp_ReadCommand( p_access, &i_answer, NULL ) > 2 ) + if( ftp_SendCommand( p_access, p_sys, "%s %s", + p_sys->out ? "STOR" : "RETR", + p_sys->url.psz_path ) < 0 || + ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) > 2 ) { - msg_Err( p_access, "cannot retreive file" ); + msg_Err( p_access, "cannot retrieve file" ); return VLC_EGENERIC; } + + shutdown( p_sys->fd_data, p_sys->out ? SHUT_RD : SHUT_WR ); + return VLC_SUCCESS; } -static int ftp_StopStream ( access_t *p_access ) +static int ftp_StopStream ( vlc_object_t *p_access, access_sys_t *p_sys ) { - access_sys_t *p_sys = p_access->p_sys; - - int i_answer; - - if( ftp_SendCommand( p_access, "ABOR" ) < 0 ) + if( ftp_SendCommand( p_access, p_sys, "ABOR" ) < 0 ) { - msg_Warn( p_access, "cannot abord file" ); + 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( p_sys->fd_data > 0 ) + + if( p_sys->fd_data != -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 ); + 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 VLC_SUCCESS; } -