1 /*****************************************************************************
2 * ftp.c: FTP input module
3 *****************************************************************************
4 * Copyright (C) 2001-2004 VideoLAN
5 * $Id: ftp.c,v 1.25 2004/01/25 17:31:22 gbazin Exp $
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
30 #include <vlc/input.h>
34 /*****************************************************************************
36 *****************************************************************************/
37 static int Open ( vlc_object_t * );
38 static void Close ( vlc_object_t * );
40 #define CACHING_TEXT N_("Caching value in ms")
41 #define CACHING_LONGTEXT N_( \
42 "Allows you to modify the default caching value for ftp streams. This " \
43 "value should be set in millisecond units." )
44 #define USER_TEXT N_("FTP user name")
45 #define USER_LONGTEXT N_("Allows you to modify the user name that will " \
46 "be used for the connection.")
47 #define PASS_TEXT N_("FTP password")
48 #define PASS_LONGTEXT N_("Allows you to modify the password that will be " \
49 "used for the connection.")
50 #define ACCOUNT_TEXT N_("FTP account")
51 #define ACCOUNT_LONGTEXT N_("Allows you to modify the account that will be " \
52 "used for the connection.")
55 set_description( _("FTP input") );
56 set_capability( "access", 0 );
57 add_integer( "ftp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL,
58 CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
59 add_string( "ftp-user", "anonymous", NULL, USER_TEXT, USER_LONGTEXT,
61 add_string( "ftp-pwd", "anonymous@dummy.org", NULL, PASS_TEXT,
62 PASS_LONGTEXT, VLC_FALSE );
63 add_string( "ftp-account", "anonymous", NULL, ACCOUNT_TEXT,
64 ACCOUNT_LONGTEXT, VLC_FALSE );
65 add_shortcut( "ftp" );
66 set_callbacks( Open, Close );
69 /*****************************************************************************
71 *****************************************************************************/
72 static ssize_t Read ( input_thread_t *, byte_t *, size_t );
73 static void Seek ( input_thread_t *, off_t );
85 static int ftp_SendCommand( input_thread_t *, char *, ... );
86 static int ftp_ReadCommand( input_thread_t *, int *, char ** );
87 static int ftp_StartStream( input_thread_t *, off_t );
88 static int ftp_StopStream ( input_thread_t *);
90 /****************************************************************************
91 * Open: connect to ftp server and ask for file
92 ****************************************************************************/
93 static int Open( vlc_object_t *p_this )
95 input_thread_t *p_input = (input_thread_t*)p_this;
103 /* *** allocate access_sys_t *** */
104 p_sys = p_input->p_access_data = malloc( sizeof( access_sys_t ) );
105 memset( p_sys, 0, sizeof( access_sys_t ) );
109 /* *** Parse URL and get server addr/port and path *** */
110 psz = p_input->psz_name;
115 vlc_UrlParse( &p_sys->url, psz, 0 );
117 if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
119 msg_Err( p_input, "invalid server name" );
122 if( p_sys->url.i_port <= 0 )
124 p_sys->url.i_port = 21; /* default port */
127 /* *** Open a TCP connection with server *** */
128 msg_Dbg( p_input, "waiting for connection..." );
129 p_sys->fd_cmd = net_OpenTCP( p_input, p_sys->url.psz_host,
131 if( p_sys->fd_cmd < 0 )
133 msg_Err( p_input, "failed to connect with server" );
140 if( ftp_ReadCommand( p_input, &i_answer, NULL ) != 1 )
145 if( i_answer / 100 != 2 )
147 msg_Err( p_input, "connection rejected" );
151 msg_Dbg( p_input, "connection accepted (%d)", i_answer );
153 var_Create( p_input, "ftp-user", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
154 var_Get( p_input, "ftp-user", &val );
155 if( ftp_SendCommand( p_input, "USER %s", val.psz_string ) < 0 ||
156 ftp_ReadCommand( p_input, &i_answer, NULL ) < 0 )
158 if( val.psz_string ) free( val.psz_string );
161 if( val.psz_string ) free( val.psz_string );
163 switch( i_answer / 100 )
166 msg_Dbg( p_input, "user accepted" );
169 msg_Dbg( p_input, "password needed" );
170 var_Create( p_input, "ftp-pwd", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
171 var_Get( p_input, "ftp-pwd", &val );
172 if( ftp_SendCommand( p_input, "PASS %s", val.psz_string ) < 0 ||
173 ftp_ReadCommand( p_input, &i_answer, NULL ) < 0 )
175 if( val.psz_string ) free( val.psz_string );
178 if( val.psz_string ) free( val.psz_string );
180 switch( i_answer / 100 )
183 msg_Dbg( p_input, "password accepted" );
186 msg_Dbg( p_input, "account needed" );
187 var_Create( p_input, "ftp-account",
188 VLC_VAR_STRING | VLC_VAR_DOINHERIT );
189 var_Get( p_input, "ftp-account", &val );
190 if( ftp_SendCommand( p_input, "ACCT %s",
191 val.psz_string ) < 0 ||
192 ftp_ReadCommand( p_input, &i_answer, NULL ) < 0 )
194 if( val.psz_string ) free( val.psz_string );
197 if( val.psz_string ) free( val.psz_string );
199 if( i_answer / 100 != 2 )
201 msg_Err( p_input, "account rejected" );
204 msg_Dbg( p_input, "account accepted" );
208 msg_Err( p_input, "password rejected" );
213 msg_Err( p_input, "user rejected" );
218 if( ftp_SendCommand( p_input, "TYPE I" ) < 0 ||
219 ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 )
221 msg_Err( p_input, "cannot set binary transfert mode" );
226 if( ftp_SendCommand( p_input, "SIZE %s", p_sys->url.psz_path ) < 0 ||
227 ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 )
229 msg_Err( p_input, "cannot get file size" );
232 p_sys->i_size = atoll( &psz_arg[4] );
234 msg_Dbg( p_input, "file size: "I64Fd, p_sys->i_size );
236 /* Start the 'stream' */
237 if( ftp_StartStream( p_input, 0 ) < 0 )
239 msg_Err( p_input, "cannot retrieve file" );
242 /* *** set exported functions *** */
243 p_input->pf_read = Read;
244 p_input->pf_seek = Seek;
245 p_input->pf_set_program = input_SetProgram;
246 p_input->pf_set_area = NULL;
248 p_input->p_private = NULL;
250 /* *** finished to set some variable *** */
251 vlc_mutex_lock( &p_input->stream.stream_lock );
252 p_input->stream.b_pace_control = VLC_TRUE;
253 p_input->stream.p_selected_area->i_tell = 0;
254 p_input->stream.b_seekable = VLC_TRUE;
255 p_input->stream.p_selected_area->i_size = p_sys->i_size;
256 p_input->stream.i_method = INPUT_METHOD_NETWORK;
257 vlc_mutex_unlock( &p_input->stream.stream_lock );
259 /* Update default_pts to a suitable value for ftp access */
260 var_Create( p_input, "ftp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
261 var_Get( p_input, "ftp-caching", &val );
262 p_input->i_pts_delay = val.i_int * 1000;
267 if( p_sys->fd_cmd > 0 )
269 net_Close( p_sys->fd_cmd );
271 vlc_UrlClean( &p_sys->url );
276 /*****************************************************************************
277 * Close: free unused data structures
278 *****************************************************************************/
279 static void Close( vlc_object_t *p_this )
281 input_thread_t *p_input = (input_thread_t *)p_this;
282 access_sys_t *p_sys = p_input->p_access_data;
284 msg_Dbg( p_input, "stopping stream" );
285 ftp_StopStream( p_input );
287 if( ftp_SendCommand( p_input, "QUIT" ) < 0 )
289 msg_Warn( p_input, "cannot quit" );
293 ftp_ReadCommand( p_input, NULL, NULL );
295 net_Close( p_sys->fd_cmd );
298 vlc_UrlClean( &p_sys->url );
302 /*****************************************************************************
303 * Seek: try to go at the right place
304 *****************************************************************************/
305 static void Seek( input_thread_t * p_input, off_t i_pos )
311 vlc_mutex_lock( &p_input->stream.stream_lock );
313 msg_Dbg( p_input, "seeking to "I64Fd, i_pos );
315 ftp_StopStream( p_input );
316 ftp_StartStream( p_input, i_pos );
318 p_input->stream.p_selected_area->i_tell = i_pos;
319 vlc_mutex_unlock( &p_input->stream.stream_lock );
322 /*****************************************************************************
324 *****************************************************************************/
325 static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer,
328 access_sys_t *p_sys = p_input->p_access_data;
330 return net_Read( p_input, p_sys->fd_data, p_buffer, i_len, VLC_FALSE );
333 /*****************************************************************************
335 *****************************************************************************/
336 static int ftp_SendCommand( input_thread_t *p_input, char *psz_fmt, ... )
338 access_sys_t *p_sys = p_input->p_access_data;
343 va_start( args, psz_fmt );
344 vasprintf( &psz_cmd, psz_fmt, args );
347 msg_Dbg( p_input, "ftp_SendCommand:\"%s\"", psz_cmd);
348 if( ( i_ret = net_Printf( VLC_OBJECT(p_input), p_sys->fd_cmd,
349 "%s", psz_cmd ) ) > 0 )
351 i_ret = net_Printf( VLC_OBJECT(p_input), p_sys->fd_cmd, "\n" );
356 msg_Err( p_input, "failed to send command" );
362 /* TODO support this s**t :
363 RFC 959 allows the client to send certain TELNET strings at any moment,
364 even in the middle of a request:
367 * \377\376x where x is one byte.
368 * \377\375x where x is one byte. The server is obliged to send \377\374x
369 * immediately after reading x.
370 * \377\374x where x is one byte.
371 * \377\373x where x is one byte. The server is obliged to send \377\376x
372 * immediately after reading x.
373 * \377x for any other byte x.
375 These strings are not part of the requests, except in the case \377\377,
376 where the request contains one \377. */
377 static int ftp_ReadCommand( input_thread_t *p_input,
378 int *pi_answer, char **ppsz_answer )
380 access_sys_t *p_sys = p_input->p_access_data;
384 psz_line = net_Gets( p_input, p_sys->fd_cmd );
385 msg_Dbg( p_input, "answer=%s", psz_line );
386 if( psz_line == NULL || strlen( psz_line ) < 3 )
388 msg_Err( p_input, "cannot get answer" );
389 if( psz_line ) free( psz_line );
390 if( pi_answer ) *pi_answer = 500;
391 if( ppsz_answer ) *ppsz_answer = NULL;
395 i_answer = atoi( psz_line );
397 if( pi_answer ) *pi_answer = i_answer;
400 *ppsz_answer = psz_line;
406 return( i_answer / 100 );
409 static int ftp_StartStream( input_thread_t *p_input, off_t i_start )
411 access_sys_t *p_sys = p_input->p_access_data;
415 char *psz_arg, *psz_parser;
420 if( ftp_SendCommand( p_input, "PASV" ) < 0 ||
421 ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 )
423 msg_Err( p_input, "cannot set passive transfert mode" );
427 psz_parser = strchr( psz_arg, '(' );
429 sscanf( psz_parser, "(%d,%d,%d,%d,%d,%d", &a1, &a2, &a3,
430 &a4, &p1, &p2 ) < 6 )
433 msg_Err( p_input, "cannot get ip/port for passive transfert mode" );
438 sprintf( psz_ip, "%d.%d.%d.%d", a1, a2, a3, a4 );
439 i_port = p1 * 256 + p2;
440 msg_Dbg( p_input, "ip:%s port:%d", psz_ip, i_port );
442 if( ftp_SendCommand( p_input, "TYPE I" ) < 0 ||
443 ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 )
445 msg_Err( p_input, "cannot set binary transfert mode" );
451 if( ftp_SendCommand( p_input, "REST "I64Fu, i_start ) < 0 ||
452 ftp_ReadCommand( p_input, &i_answer, NULL ) > 3 )
454 msg_Err( p_input, "cannot set restart point" );
459 msg_Dbg( p_input, "waiting for data connection..." );
460 p_sys->fd_data = net_OpenTCP( p_input, psz_ip, i_port );
461 if( p_sys->fd_data < 0 )
463 msg_Err( p_input, "failed to connect with server" );
466 msg_Dbg( p_input, "connection with \"%s:%d\" successful",
470 if( ftp_SendCommand( p_input, "RETR %s", p_sys->url.psz_path ) < 0 ||
471 ftp_ReadCommand( p_input, &i_answer, NULL ) > 2 )
473 msg_Err( p_input, "cannot retreive file" );
479 static int ftp_StopStream ( input_thread_t *p_input)
481 access_sys_t *p_sys = p_input->p_access_data;
485 if( ftp_SendCommand( p_input, "ABOR" ) < 0 )
487 msg_Warn( p_input, "cannot abord file" );
488 net_Close( p_sys->fd_data ); p_sys->fd_data = -1;
491 net_Close( p_sys->fd_data ); p_sys->fd_data = -1;
492 ftp_ReadCommand( p_input, &i_answer, NULL );
493 ftp_ReadCommand( p_input, &i_answer, NULL );