~ubuntu-branches/debian/wheezy/vlc/wheezy

« back to all changes in this revision

Viewing changes to modules/access/ftp.c

Tags: upstream-0.7.2.final
ImportĀ upstreamĀ versionĀ 0.7.2.final

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 $
 
6
 *
 
7
 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
 
8
 *
 
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.
 
13
 *
 
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.
 
18
 *
 
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
 *****************************************************************************/
 
23
 
 
24
/*****************************************************************************
 
25
 * Preamble
 
26
 *****************************************************************************/
 
27
#include <stdlib.h>
 
28
 
 
29
#include <vlc/vlc.h>
 
30
#include <vlc/input.h>
 
31
 
 
32
#include "network.h"
 
33
 
 
34
/*****************************************************************************
 
35
 * Module descriptor
 
36
 *****************************************************************************/
 
37
static int     Open     ( vlc_object_t * );
 
38
static void    Close    ( vlc_object_t * );
 
39
 
 
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.")
 
53
 
 
54
vlc_module_begin();
 
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,
 
60
                VLC_FALSE );
 
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 );
 
67
vlc_module_end();
 
68
 
 
69
/*****************************************************************************
 
70
 * Local prototypes
 
71
 *****************************************************************************/
 
72
static ssize_t Read     ( input_thread_t *, byte_t *,  size_t );
 
73
static void    Seek     ( input_thread_t *, off_t );
 
74
 
 
75
struct access_sys_t
 
76
{
 
77
    vlc_url_t url;
 
78
 
 
79
    int       fd_cmd;
 
80
    int       fd_data;
 
81
 
 
82
    int64_t   i_size;
 
83
};
 
84
 
 
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 *);
 
89
 
 
90
/****************************************************************************
 
91
 * Open: connect to ftp server and ask for file
 
92
 ****************************************************************************/
 
93
static int Open( vlc_object_t *p_this )
 
94
{
 
95
    input_thread_t  *p_input = (input_thread_t*)p_this;
 
96
    access_sys_t    *p_sys;
 
97
    char            *psz;
 
98
    vlc_value_t     val;
 
99
 
 
100
    int             i_answer;
 
101
    char            *psz_arg;
 
102
 
 
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 ) );
 
106
    p_sys->fd_cmd = -1;
 
107
    p_sys->fd_data = -1;
 
108
 
 
109
    /* *** Parse URL and get server addr/port and path *** */
 
110
    psz = p_input->psz_name;
 
111
    while( *psz == '/' )
 
112
    {
 
113
        psz++;
 
114
    }
 
115
    vlc_UrlParse( &p_sys->url, psz, 0 );
 
116
 
 
117
    if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
 
118
    {
 
119
        msg_Err( p_input, "invalid server name" );
 
120
        goto exit_error;
 
121
    }
 
122
    if( p_sys->url.i_port <= 0 )
 
123
    {
 
124
        p_sys->url.i_port = 21; /* default port */
 
125
    }
 
126
 
 
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,
 
130
                                 p_sys->url.i_port );
 
131
    if( p_sys->fd_cmd < 0 )
 
132
    {
 
133
        msg_Err( p_input, "failed to connect with server" );
 
134
        goto exit_error;
 
135
    }
 
136
    p_input->i_mtu = 0;
 
137
 
 
138
    for( ;; )
 
139
    {
 
140
        if( ftp_ReadCommand( p_input, &i_answer, NULL ) != 1 )
 
141
        {
 
142
            break;
 
143
        }
 
144
    }
 
145
    if( i_answer / 100 != 2 )
 
146
    {
 
147
        msg_Err( p_input, "connection rejected" );
 
148
        goto exit_error;
 
149
    }
 
150
 
 
151
    msg_Dbg( p_input, "connection accepted (%d)", i_answer );
 
152
 
 
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 )
 
157
    {
 
158
        if( val.psz_string ) free( val.psz_string );
 
159
        goto exit_error;
 
160
    }
 
161
    if( val.psz_string ) free( val.psz_string );
 
162
 
 
163
    switch( i_answer / 100 )
 
164
    {
 
165
        case 2:
 
166
            msg_Dbg( p_input, "user accepted" );
 
167
            break;
 
168
        case 3:
 
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 )
 
174
            {
 
175
                if( val.psz_string ) free( val.psz_string );
 
176
                goto exit_error;
 
177
            }
 
178
            if( val.psz_string ) free( val.psz_string );
 
179
 
 
180
            switch( i_answer / 100 )
 
181
            {
 
182
                case 2:
 
183
                    msg_Dbg( p_input, "password accepted" );
 
184
                    break;
 
185
                case 3:
 
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 )
 
193
                    {
 
194
                        if( val.psz_string ) free( val.psz_string );
 
195
                        goto exit_error;
 
196
                    }
 
197
                    if( val.psz_string ) free( val.psz_string );
 
198
 
 
199
                    if( i_answer / 100 != 2 )
 
200
                    {
 
201
                        msg_Err( p_input, "account rejected" );
 
202
                        goto exit_error;
 
203
                    }
 
204
                    msg_Dbg( p_input, "account accepted" );
 
205
                    break;
 
206
 
 
207
                default:
 
208
                    msg_Err( p_input, "password rejected" );
 
209
                    goto exit_error;
 
210
            }
 
211
            break;
 
212
        default:
 
213
            msg_Err( p_input, "user rejected" );
 
214
            goto exit_error;
 
215
    }
 
216
 
 
217
    /* binary mode */
 
218
    if( ftp_SendCommand( p_input, "TYPE I" ) < 0 ||
 
219
        ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 )
 
220
    {
 
221
        msg_Err( p_input, "cannot set binary transfert mode" );
 
222
        goto exit_error;
 
223
    }
 
224
 
 
225
    /* get size */
 
226
    if( ftp_SendCommand( p_input, "SIZE %s", p_sys->url.psz_path ) < 0 ||
 
227
        ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 )
 
228
    {
 
229
        msg_Err( p_input, "cannot get file size" );
 
230
        goto exit_error;
 
231
    }
 
232
    p_sys->i_size = atoll( &psz_arg[4] );
 
233
    free( psz_arg );
 
234
    msg_Dbg( p_input, "file size: "I64Fd, p_sys->i_size );
 
235
 
 
236
    /* Start the 'stream' */
 
237
    if( ftp_StartStream( p_input, 0 ) < 0 )
 
238
    {
 
239
        msg_Err( p_input, "cannot retrieve file" );
 
240
        goto exit_error;
 
241
    }
 
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;
 
247
 
 
248
    p_input->p_private = NULL;
 
249
 
 
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 );
 
258
 
 
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;
 
263
 
 
264
    return VLC_SUCCESS;
 
265
 
 
266
exit_error:
 
267
    if( p_sys->fd_cmd > 0 )
 
268
    {
 
269
        net_Close( p_sys->fd_cmd );
 
270
    }
 
271
    vlc_UrlClean( &p_sys->url );
 
272
    free( p_sys );
 
273
    return VLC_EGENERIC;
 
274
}
 
275
 
 
276
/*****************************************************************************
 
277
 * Close: free unused data structures
 
278
 *****************************************************************************/
 
279
static void Close( vlc_object_t *p_this )
 
280
{
 
281
    input_thread_t  *p_input = (input_thread_t *)p_this;
 
282
    access_sys_t    *p_sys = p_input->p_access_data;
 
283
 
 
284
    msg_Dbg( p_input, "stopping stream" );
 
285
    ftp_StopStream( p_input );
 
286
 
 
287
    if( ftp_SendCommand( p_input, "QUIT" ) < 0 )
 
288
    {
 
289
        msg_Warn( p_input, "cannot quit" );
 
290
    }
 
291
    else
 
292
    {
 
293
        ftp_ReadCommand( p_input, NULL, NULL );
 
294
    }
 
295
    net_Close( p_sys->fd_cmd );
 
296
 
 
297
    /* free memory */
 
298
    vlc_UrlClean( &p_sys->url );
 
299
    free( p_sys );
 
300
}
 
301
 
 
302
/*****************************************************************************
 
303
 * Seek: try to go at the right place
 
304
 *****************************************************************************/
 
305
static void Seek( input_thread_t * p_input, off_t i_pos )
 
306
{
 
307
    if( i_pos < 0 )
 
308
    {
 
309
        return;
 
310
    }
 
311
    vlc_mutex_lock( &p_input->stream.stream_lock );
 
312
 
 
313
    msg_Dbg( p_input, "seeking to "I64Fd, i_pos );
 
314
 
 
315
    ftp_StopStream( p_input );
 
316
    ftp_StartStream( p_input, i_pos );
 
317
 
 
318
    p_input->stream.p_selected_area->i_tell = i_pos;
 
319
    vlc_mutex_unlock( &p_input->stream.stream_lock );
 
320
}
 
321
 
 
322
/*****************************************************************************
 
323
 * Read:
 
324
 *****************************************************************************/
 
325
static ssize_t Read( input_thread_t * p_input, byte_t * p_buffer,
 
326
                     size_t i_len )
 
327
{
 
328
    access_sys_t *p_sys = p_input->p_access_data;
 
329
 
 
330
    return net_Read( p_input, p_sys->fd_data, p_buffer, i_len, VLC_FALSE );
 
331
}
 
332
 
 
333
/*****************************************************************************
 
334
 * ftp_*:
 
335
 *****************************************************************************/
 
336
static int ftp_SendCommand( input_thread_t *p_input, char *psz_fmt, ... )
 
337
{
 
338
    access_sys_t *p_sys = p_input->p_access_data;
 
339
    va_list      args;
 
340
    char         *psz_cmd;
 
341
    int          i_ret;
 
342
 
 
343
    va_start( args, psz_fmt );
 
344
    vasprintf( &psz_cmd, psz_fmt, args );
 
345
    va_end( args );
 
346
 
 
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 )
 
350
    {
 
351
        i_ret = net_Printf( VLC_OBJECT(p_input), p_sys->fd_cmd, "\n" );
 
352
    }
 
353
 
 
354
    if( i_ret < 0 )
 
355
    {
 
356
        msg_Err( p_input, "failed to send command" );
 
357
        return VLC_EGENERIC;
 
358
    }
 
359
    return VLC_SUCCESS;
 
360
}
 
361
 
 
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:
 
365
 
 
366
 * \377\377.
 
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.
 
374
 
 
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 )
 
379
{
 
380
    access_sys_t *p_sys = p_input->p_access_data;
 
381
    char         *psz_line;
 
382
    int          i_answer;
 
383
 
 
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 )
 
387
    {
 
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;
 
392
        return -1;
 
393
    }
 
394
 
 
395
    i_answer = atoi( psz_line );
 
396
 
 
397
    if( pi_answer ) *pi_answer = i_answer;
 
398
    if( ppsz_answer )
 
399
    {
 
400
        *ppsz_answer = psz_line;
 
401
    }
 
402
    else
 
403
    {
 
404
        free( psz_line );
 
405
    }
 
406
    return( i_answer / 100 );
 
407
}
 
408
 
 
409
static int ftp_StartStream( input_thread_t *p_input, off_t i_start )
 
410
{
 
411
    access_sys_t *p_sys = p_input->p_access_data;
 
412
 
 
413
    char psz_ip[1000];
 
414
    int  i_answer;
 
415
    char *psz_arg, *psz_parser;
 
416
    int  a1,a2,a3,a4;
 
417
    int  p1,p2;
 
418
    int  i_port;
 
419
 
 
420
    if( ftp_SendCommand( p_input, "PASV" ) < 0 ||
 
421
        ftp_ReadCommand( p_input, &i_answer, &psz_arg ) != 2 )
 
422
    {
 
423
        msg_Err( p_input, "cannot set passive transfert mode" );
 
424
        return VLC_EGENERIC;
 
425
    }
 
426
 
 
427
    psz_parser = strchr( psz_arg, '(' );
 
428
    if( !psz_parser ||
 
429
        sscanf( psz_parser, "(%d,%d,%d,%d,%d,%d", &a1, &a2, &a3,
 
430
                &a4, &p1, &p2 ) < 6 )
 
431
    {
 
432
        free( psz_arg );
 
433
        msg_Err( p_input, "cannot get ip/port for passive transfert mode" );
 
434
        return VLC_EGENERIC;
 
435
    }
 
436
    free( psz_arg );
 
437
 
 
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 );
 
441
 
 
442
    if( ftp_SendCommand( p_input, "TYPE I" ) < 0 ||
 
443
        ftp_ReadCommand( p_input, &i_answer, NULL ) != 2 )
 
444
    {
 
445
        msg_Err( p_input, "cannot set binary transfert mode" );
 
446
        return VLC_EGENERIC;
 
447
    }
 
448
 
 
449
    if( i_start > 0 )
 
450
    {
 
451
        if( ftp_SendCommand( p_input, "REST "I64Fu, i_start ) < 0 ||
 
452
            ftp_ReadCommand( p_input, &i_answer, NULL ) > 3 )
 
453
        {
 
454
            msg_Err( p_input, "cannot set restart point" );
 
455
            return VLC_EGENERIC;
 
456
        }
 
457
    }
 
458
 
 
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 )
 
462
    {
 
463
        msg_Err( p_input, "failed to connect with server" );
 
464
        return VLC_EGENERIC;
 
465
    }
 
466
    msg_Dbg( p_input, "connection with \"%s:%d\" successful",
 
467
             psz_ip, i_port );
 
468
 
 
469
    /* "1xx" message */
 
470
    if( ftp_SendCommand( p_input, "RETR %s", p_sys->url.psz_path ) < 0 ||
 
471
        ftp_ReadCommand( p_input, &i_answer, NULL ) > 2 )
 
472
    {
 
473
        msg_Err( p_input, "cannot retreive file" );
 
474
        return VLC_EGENERIC;
 
475
    }
 
476
    return VLC_SUCCESS;
 
477
}
 
478
 
 
479
static int ftp_StopStream ( input_thread_t *p_input)
 
480
{
 
481
    access_sys_t *p_sys = p_input->p_access_data;
 
482
 
 
483
    int i_answer;
 
484
 
 
485
    if( ftp_SendCommand( p_input, "ABOR" ) < 0 )
 
486
    {
 
487
        msg_Warn( p_input, "cannot abord file" );
 
488
        net_Close( p_sys->fd_data ); p_sys->fd_data = -1;
 
489
        return VLC_EGENERIC;
 
490
    }
 
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 );
 
494
 
 
495
    return VLC_SUCCESS;
 
496
}