1
/*****************************************************************************
2
* ftp.c: FTP input module
3
*****************************************************************************
4
* Copyright (C) 2001-2004 VideoLAN
5
* $Id: ftp.c 7522 2004-04-27 16:35:15Z sam $
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 );