Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

ftp.c

00001 /*****************************************************************************
00002  * ftp.c: FTP input module
00003  *****************************************************************************
00004  * Copyright (C) 2001-2005 the VideoLAN team
00005  * $Id: ftp.c 12956 2005-10-25 07:02:52Z md $
00006  *
00007  * Authors: Laurent Aimar <[email protected]> - original code
00008  *          Rémi Denis-Courmont <rem # videolan.org> - EPSV support
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00023  *****************************************************************************/
00024 
00025 /*****************************************************************************
00026  * Preamble
00027  *****************************************************************************/
00028 #include <stdlib.h>
00029 
00030 #include <vlc/vlc.h>
00031 #include <vlc/input.h>
00032 
00033 #include "network.h"
00034 #if defined( UNDER_CE )
00035 #   include <winsock.h>
00036 #elif defined( WIN32 )
00037 #   include <winsock2.h>
00038 #else
00039 #   include <sys/socket.h>
00040 #endif
00041 
00042 /*****************************************************************************
00043  * Module descriptor
00044  *****************************************************************************/
00045 static int     Open ( vlc_object_t * );
00046 static void    Close( vlc_object_t * );
00047 
00048 #define CACHING_TEXT N_("Caching value in ms")
00049 #define CACHING_LONGTEXT N_( \
00050     "Allows you to modify the default caching value for FTP streams. This " \
00051     "value should be set in millisecond units." )
00052 #define USER_TEXT N_("FTP user name")
00053 #define USER_LONGTEXT N_("Allows you to modify the user name that will " \
00054     "be used for the connection.")
00055 #define PASS_TEXT N_("FTP password")
00056 #define PASS_LONGTEXT N_("Allows you to modify the password that will be " \
00057     "used for the connection.")
00058 #define ACCOUNT_TEXT N_("FTP account")
00059 #define ACCOUNT_LONGTEXT N_("Allows you to modify the account that will be " \
00060     "used for the connection.")
00061 
00062 vlc_module_begin();
00063     set_shortname( "FTP" );
00064     set_description( _("FTP input") );
00065     set_capability( "access2", 0 );
00066     set_category( CAT_INPUT );
00067     set_subcategory( SUBCAT_INPUT_ACCESS );
00068     add_integer( "ftp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL,
00069                  CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
00070     add_string( "ftp-user", "anonymous", NULL, USER_TEXT, USER_LONGTEXT,
00071                 VLC_FALSE );
00072     add_string( "ftp-pwd", "[email protected]", NULL, PASS_TEXT,
00073                 PASS_LONGTEXT, VLC_FALSE );
00074     add_string( "ftp-account", "anonymous", NULL, ACCOUNT_TEXT,
00075                 ACCOUNT_LONGTEXT, VLC_FALSE );
00076     add_shortcut( "ftp" );
00077     set_callbacks( Open, Close );
00078 vlc_module_end();
00079 
00080 /*****************************************************************************
00081  * Local prototypes
00082  *****************************************************************************/
00083 static int Read( access_t *, uint8_t *, int );
00084 static int Seek( access_t *, int64_t );
00085 static int Control( access_t *, int, va_list );
00086 
00087 struct access_sys_t
00088 {
00089     vlc_url_t  url;
00090 
00091     int        fd_cmd;
00092     int        fd_data;
00093     
00094     char       sz_epsv_ip[NI_MAXNUMERICHOST];
00095 };
00096 
00097 static int  ftp_SendCommand( access_t *, char *, ... );
00098 static int  ftp_ReadCommand( access_t *, int *, char ** );
00099 static int  ftp_StartStream( access_t *, int64_t );
00100 static int  ftp_StopStream ( access_t *);
00101 
00102 static int Connect( access_t *p_access, access_sys_t *p_sys )
00103 {
00104     int fd, i_answer;
00105     char *psz;
00106 
00107     /* *** Open a TCP connection with server *** */
00108     msg_Dbg( p_access, "waiting for connection..." );
00109     p_sys->fd_cmd = fd = net_OpenTCP( p_access, p_sys->url.psz_host,
00110                                       p_sys->url.i_port );
00111     if( fd < 0 )
00112     {
00113         msg_Err( p_access, "failed to connect with server" );
00114         return -1;
00115     }
00116 
00117     for( ;; )
00118     {
00119         if( ftp_ReadCommand( p_access, &i_answer, NULL ) != 1 )
00120         {
00121             break;
00122         }
00123     }
00124     if( i_answer / 100 != 2 )
00125     {
00126         msg_Err( p_access, "connection rejected" );
00127         return -1;
00128     }
00129 
00130     msg_Dbg( p_access, "connection accepted (%d)", i_answer );
00131 
00132     psz = var_CreateGetString( p_access, "ftp-user" );
00133     if( ftp_SendCommand( p_access, "USER %s", psz ) < 0 ||
00134         ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
00135     {
00136         free( psz );
00137         return -1;
00138     }
00139     free( psz );
00140 
00141     switch( i_answer / 100 )
00142     {
00143         case 2:
00144             msg_Dbg( p_access, "user accepted" );
00145             break;
00146         case 3:
00147             msg_Dbg( p_access, "password needed" );
00148             psz = var_CreateGetString( p_access, "ftp-pwd" );
00149             if( ftp_SendCommand( p_access, "PASS %s", psz ) < 0 ||
00150                 ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
00151             {
00152                 free( psz );
00153                 return -1;
00154             }
00155             free( psz );
00156 
00157             switch( i_answer / 100 )
00158             {
00159                 case 2:
00160                     msg_Dbg( p_access, "password accepted" );
00161                     break;
00162                 case 3:
00163                     msg_Dbg( p_access, "account needed" );
00164                     psz = var_CreateGetString( p_access, "ftp-account" );
00165                     if( ftp_SendCommand( p_access, "ACCT %s",
00166                                          psz ) < 0 ||
00167                         ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
00168                     {
00169                         free( psz );
00170                         return -1;
00171                     }
00172                     free( psz );
00173 
00174                     if( i_answer / 100 != 2 )
00175                     {
00176                         msg_Err( p_access, "account rejected" );
00177                         return -1;
00178                     }
00179                     msg_Dbg( p_access, "account accepted" );
00180                     break;
00181 
00182                 default:
00183                     msg_Err( p_access, "password rejected" );
00184                     return -1;
00185             }
00186             break;
00187         default:
00188             msg_Err( p_access, "user rejected" );
00189             return -1;
00190     }
00191 
00192     return 0;
00193 }
00194 
00195 /****************************************************************************
00196  * Open: connect to ftp server and ask for file
00197  ****************************************************************************/
00198 static int Open( vlc_object_t *p_this )
00199 {
00200     access_t     *p_access = (access_t*)p_this;
00201     access_sys_t *p_sys;
00202     char         *psz;
00203 
00204     int          i_answer;
00205     char         *psz_arg;
00206 
00207     /* Init p_access */
00208     p_access->pf_read = Read;
00209     p_access->pf_block = NULL;
00210     p_access->pf_seek = Seek;
00211     p_access->pf_control = Control;
00212     p_access->info.i_update = 0;
00213     p_access->info.i_size = 0;
00214     p_access->info.i_pos = 0;
00215     p_access->info.b_eof = VLC_FALSE;
00216     p_access->info.i_title = 0;
00217     p_access->info.i_seekpoint = 0;
00218     p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
00219     memset( p_sys, 0, sizeof( access_sys_t ) );
00220     p_sys->fd_cmd = -1;
00221     p_sys->fd_data = -1;
00222 
00223     /* *** Parse URL and get server addr/port and path *** */
00224     psz = p_access->psz_path;
00225     while( *psz == '/' )
00226     {
00227         psz++;
00228     }
00229     vlc_UrlParse( &p_sys->url, psz, 0 );
00230 
00231     if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '\0' )
00232     {
00233         msg_Err( p_access, "invalid server name" );
00234         goto exit_error;
00235     }
00236     if( p_sys->url.i_port <= 0 )
00237     {
00238         p_sys->url.i_port = 21; /* default port */
00239     }
00240 
00241     /* FTP URLs are relative to user's default directory (RFC1738)
00242        For absolute path use ftp://foo.bar//usr/local/etc/filename */
00243 
00244     if( *p_sys->url.psz_path == '/' )
00245         p_sys->url.psz_path++;
00246 
00247     if( Connect( p_access, p_sys ) < 0 )
00248         goto exit_error;
00249 
00250     /* Extended passive mode */
00251     if( ftp_SendCommand( p_access, "EPSV ALL" ) < 0 )
00252     {
00253         msg_Err( p_access, "cannot request extended passive mode" );
00254         return -1;
00255     }
00256 
00257     if( ftp_ReadCommand( p_access, &i_answer, NULL ) == 2 )
00258     {
00259         if( net_GetPeerAddress( p_sys->fd_cmd, p_sys->sz_epsv_ip, NULL ) )
00260             goto exit_error;
00261     }
00262     else
00263     {
00264         /* If ESPV ALL fails, we fallback to PASV.
00265          * We have to restart the connection in case there is a NAT that
00266          * understands EPSV ALL in the way, and hence won't allow PASV on
00267          * the initial connection.
00268          */
00269         net_Close( p_sys->fd_cmd );
00270         p_sys->fd_cmd = -1;
00271         *p_sys->sz_epsv_ip = '\0';
00272 
00273         if( ( p_sys->fd_cmd = Connect( p_access, p_sys ) ) < 0 )
00274            goto exit_error;
00275 
00276         msg_Info( p_access, "FTP Extended passive mode disabled" );
00277     }
00278     
00279     /* binary mode */
00280     if( ftp_SendCommand( p_access, "TYPE I" ) < 0 ||
00281         ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 )
00282     {
00283         msg_Err( p_access, "cannot set binary transfer mode" );
00284         goto exit_error;
00285     }
00286 
00287     /* get size */
00288     if( ftp_SendCommand( p_access, "SIZE %s", p_sys->url.psz_path ) < 0 ||
00289         ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 )
00290     {
00291         msg_Err( p_access, "cannot get file size" );
00292         goto exit_error;
00293     }
00294     p_access->info.i_size = atoll( &psz_arg[4] );
00295     free( psz_arg );
00296     msg_Dbg( p_access, "file size: "I64Fd, p_access->info.i_size );
00297 
00298     /* Start the 'stream' */
00299     if( ftp_StartStream( p_access, 0 ) < 0 )
00300     {
00301         msg_Err( p_access, "cannot retrieve file" );
00302         goto exit_error;
00303     }
00304 
00305     /* Update default_pts to a suitable value for ftp access */
00306     var_Create( p_access, "ftp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
00307 
00308     return VLC_SUCCESS;
00309 
00310 exit_error:
00311     if( p_sys->fd_cmd >= 0 )
00312         net_Close( p_sys->fd_cmd );
00313     vlc_UrlClean( &p_sys->url );
00314     free( p_sys );
00315     return VLC_EGENERIC;
00316 }
00317 
00318 /*****************************************************************************
00319  * Close: free unused data structures
00320  *****************************************************************************/
00321 static void Close( vlc_object_t *p_this )
00322 {
00323     access_t      *p_access = (access_t*)p_this;
00324     access_sys_t  *p_sys = p_access->p_sys;
00325 
00326     msg_Dbg( p_access, "stopping stream" );
00327     ftp_StopStream( p_access );
00328 
00329     if( ftp_SendCommand( p_access, "QUIT" ) < 0 )
00330     {
00331         msg_Warn( p_access, "cannot quit" );
00332     }
00333     else
00334     {
00335         ftp_ReadCommand( p_access, NULL, NULL );
00336     }
00337     net_Close( p_sys->fd_cmd );
00338 
00339     /* free memory */
00340     vlc_UrlClean( &p_sys->url );
00341     free( p_sys );
00342 }
00343 
00344 /*****************************************************************************
00345  * Seek: try to go at the right place
00346  *****************************************************************************/
00347 static int Seek( access_t *p_access, int64_t i_pos )
00348 {
00349     if( i_pos < 0 )
00350     {
00351         return VLC_EGENERIC;
00352     }
00353     msg_Dbg( p_access, "seeking to "I64Fd, i_pos );
00354 
00355     ftp_StopStream( p_access );
00356     if( ftp_StartStream( p_access, i_pos ) < 0 )
00357     {
00358         p_access->info.b_eof = VLC_TRUE;
00359         return VLC_EGENERIC;
00360     }
00361 
00362     p_access->info.b_eof = VLC_FALSE;
00363     p_access->info.i_pos = i_pos;
00364 
00365     return VLC_SUCCESS;
00366 }
00367 
00368 /*****************************************************************************
00369  * Read:
00370  *****************************************************************************/
00371 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
00372 {
00373     access_sys_t *p_sys = p_access->p_sys;
00374     int i_read;
00375 
00376     if( p_access->info.b_eof )
00377         return 0;
00378 
00379     i_read = net_Read( p_access, p_sys->fd_data, NULL, p_buffer, i_len,
00380                        VLC_FALSE );
00381     if( i_read == 0 )
00382         p_access->info.b_eof = VLC_TRUE;
00383     else if( i_read > 0 )
00384         p_access->info.i_pos += i_read;
00385 
00386     return i_read;
00387 }
00388 
00389 /*****************************************************************************
00390  * Control:
00391  *****************************************************************************/
00392 static int Control( access_t *p_access, int i_query, va_list args )
00393 {
00394     vlc_bool_t   *pb_bool;
00395     int          *pi_int;
00396     int64_t      *pi_64;
00397     vlc_value_t  val;
00398 
00399     switch( i_query )
00400     {
00401         /* */
00402         case ACCESS_CAN_SEEK:
00403             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00404             *pb_bool = VLC_TRUE;
00405             break;
00406         case ACCESS_CAN_FASTSEEK:
00407             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00408             *pb_bool = VLC_FALSE;
00409             break;
00410         case ACCESS_CAN_PAUSE:
00411             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00412             *pb_bool = VLC_TRUE;    /* FIXME */
00413             break;
00414         case ACCESS_CAN_CONTROL_PACE:
00415             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
00416             *pb_bool = VLC_TRUE;    /* FIXME */
00417             break;
00418 
00419         /* */
00420         case ACCESS_GET_MTU:
00421             pi_int = (int*)va_arg( args, int * );
00422             *pi_int = 0;
00423             break;
00424 
00425         case ACCESS_GET_PTS_DELAY:
00426             pi_64 = (int64_t*)va_arg( args, int64_t * );
00427             var_Get( p_access, "ftp-caching", &val );
00428             *pi_64 = (int64_t)var_GetInteger( p_access, "ftp-caching" ) * I64C(1000);
00429             break;
00430 
00431         /* */
00432         case ACCESS_SET_PAUSE_STATE:
00433             /* Nothing to do */
00434             break;
00435 
00436         case ACCESS_GET_TITLE_INFO:
00437         case ACCESS_SET_TITLE:
00438         case ACCESS_SET_SEEKPOINT:
00439         case ACCESS_SET_PRIVATE_ID_STATE:
00440             return VLC_EGENERIC;
00441 
00442         default:
00443             msg_Warn( p_access, "unimplemented query in control" );
00444             return VLC_EGENERIC;
00445 
00446     }
00447     return VLC_SUCCESS;
00448 }
00449 
00450 /*****************************************************************************
00451  * ftp_*:
00452  *****************************************************************************/
00453 static int ftp_SendCommand( access_t *p_access, char *psz_fmt, ... )
00454 {
00455     access_sys_t *p_sys = p_access->p_sys;
00456     va_list      args;
00457     char         *psz_cmd;
00458 
00459     va_start( args, psz_fmt );
00460     vasprintf( &psz_cmd, psz_fmt, args );
00461     va_end( args );
00462 
00463     msg_Dbg( p_access, "ftp_SendCommand:\"%s\"", psz_cmd);
00464     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd_cmd, NULL, "%s\r\n",
00465                     psz_cmd ) < 0 )
00466     {
00467         msg_Err( p_access, "failed to send command" );
00468         return VLC_EGENERIC;
00469     }
00470     return VLC_SUCCESS;
00471 }
00472 
00473 /* TODO support this s**t :
00474  RFC 959 allows the client to send certain TELNET strings at any moment,
00475  even in the middle of a request:
00476 
00477  * \377\377.
00478  * \377\376x where x is one byte.
00479  * \377\375x where x is one byte. The server is obliged to send \377\374x
00480  *                                immediately after reading x.
00481  * \377\374x where x is one byte.
00482  * \377\373x where x is one byte. The server is obliged to send \377\376x
00483  *                                immediately after reading x.
00484  * \377x for any other byte x.
00485 
00486  These strings are not part of the requests, except in the case \377\377,
00487  where the request contains one \377. */
00488 static int ftp_ReadCommand( access_t *p_access,
00489                             int *pi_answer, char **ppsz_answer )
00490 {
00491     access_sys_t *p_sys = p_access->p_sys;
00492     char         *psz_line;
00493     int          i_answer;
00494 
00495     psz_line = net_Gets( p_access, p_sys->fd_cmd, NULL );
00496     msg_Dbg( p_access, "answer=%s", psz_line );
00497     if( psz_line == NULL || strlen( psz_line ) < 3 )
00498     {
00499         msg_Err( p_access, "cannot get answer" );
00500         if( psz_line ) free( psz_line );
00501         if( pi_answer ) *pi_answer    = 500;
00502         if( ppsz_answer ) *ppsz_answer  = NULL;
00503         return -1;
00504     }
00505 
00506     if( psz_line[3] == '-' )    /* Multiple response */
00507     {
00508         char end[4];
00509 
00510         memcpy( end, psz_line, 3 );
00511         end[3] = ' ';
00512 
00513         for( ;; )
00514         {
00515             char *psz_tmp = net_Gets( p_access, p_sys->fd_cmd, NULL );
00516 
00517             if( psz_tmp == NULL )   /* Error */
00518                 break;
00519 
00520             if( !strncmp( psz_tmp, end, 4 ) )
00521             {
00522                 free( psz_tmp );
00523                 break;
00524             }
00525             free( psz_tmp );
00526         }
00527     }
00528 
00529     i_answer = atoi( psz_line );
00530 
00531     if( pi_answer ) *pi_answer = i_answer;
00532     if( ppsz_answer )
00533     {
00534         *ppsz_answer = psz_line;
00535     }
00536     else
00537     {
00538         free( psz_line );
00539     }
00540     return( i_answer / 100 );
00541 }
00542 
00543 static int ftp_StartStream( access_t *p_access, off_t i_start )
00544 {
00545     access_sys_t *p_sys = p_access->p_sys;
00546 
00547     char psz_ipv4[16], *psz_ip;
00548     int  i_answer;
00549     char *psz_arg, *psz_parser;
00550     int  i_port;
00551 
00552     psz_ip = p_sys->sz_epsv_ip;
00553 
00554     if( ( ftp_SendCommand( p_access, *psz_ip ? "EPSV" : "PASV" ) < 0 )
00555      || ( ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 ) )
00556     {
00557         msg_Err( p_access, "cannot set passive mode" );
00558         return VLC_EGENERIC;
00559     }
00560 
00561     psz_parser = strchr( psz_arg, '(' );
00562     if( psz_parser == NULL )
00563     {
00564         free( psz_arg );
00565         msg_Err( p_access, "cannot parse passive mode response" );
00566         return VLC_EGENERIC;
00567     }
00568 
00569     if( psz_ip != NULL )
00570     {
00571         char psz_fmt[7] = "(|||%u";
00572         psz_fmt[1] = psz_fmt[2] = psz_fmt[3] = psz_parser[1];
00573 
00574         if( sscanf( psz_parser, psz_fmt, &i_port ) < 1 )
00575         {
00576             free( psz_arg );
00577             msg_Err( p_access, "cannot parse passive mode response" );
00578             return VLC_EGENERIC;
00579         }
00580     }
00581     else
00582     {
00583         unsigned  a1, a2, a3, a4, p1, p2;
00584 
00585         if( ( sscanf( psz_parser, "(%u,%u,%u,%u,%u,%u", &a1, &a2, &a3, &a4,
00586                       &p1, &p2 ) < 6 ) || ( a1 > 255 ) || ( a2 > 255 )
00587          || ( a3 > 255 ) || ( a4 > 255 ) || ( p1 > 255 ) || ( p2 > 255 ) )
00588         {
00589             free( psz_arg );
00590             msg_Err( p_access, "cannot parse passive mode response" );
00591             return VLC_EGENERIC;
00592         }
00593 
00594         sprintf( psz_ipv4, "%u.%u.%u.%u", a1, a2, a3, a4 );
00595         psz_ip = psz_ipv4;
00596         i_port = (p1 << 8) | p2;
00597     }
00598     free( psz_arg );
00599 
00600     msg_Dbg( p_access, "ip:%s port:%d", psz_ip, i_port );
00601 
00602     if( ftp_SendCommand( p_access, "TYPE I" ) < 0 ||
00603         ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 )
00604     {
00605         msg_Err( p_access, "cannot set binary transfer mode" );
00606         return VLC_EGENERIC;
00607     }
00608 
00609     if( i_start > 0 )
00610     {
00611         if( ftp_SendCommand( p_access, "REST "I64Fu, i_start ) < 0 ||
00612             ftp_ReadCommand( p_access, &i_answer, NULL ) > 3 )
00613         {
00614             msg_Err( p_access, "cannot set restart point" );
00615             return VLC_EGENERIC;
00616         }
00617     }
00618 
00619     msg_Dbg( p_access, "waiting for data connection..." );
00620     p_sys->fd_data = net_OpenTCP( p_access, psz_ip, i_port );
00621     if( p_sys->fd_data < 0 )
00622     {
00623         msg_Err( p_access, "failed to connect with server" );
00624         return VLC_EGENERIC;
00625     }
00626     msg_Dbg( p_access, "connection with \"%s:%d\" successful",
00627              psz_ip, i_port );
00628 
00629     /* "1xx" message */
00630     if( ftp_SendCommand( p_access, "RETR %s", p_sys->url.psz_path ) < 0 ||
00631         ftp_ReadCommand( p_access, &i_answer, NULL ) > 2 )
00632     {
00633         msg_Err( p_access, "cannot retreive file" );
00634         return VLC_EGENERIC;
00635     }
00636     return VLC_SUCCESS;
00637 }
00638 
00639 static int ftp_StopStream ( access_t *p_access )
00640 {
00641     access_sys_t *p_sys = p_access->p_sys;
00642 
00643     int i_answer;
00644 
00645     if( ftp_SendCommand( p_access, "ABOR" ) < 0 )
00646     {
00647         msg_Warn( p_access, "cannot abort file" );
00648         if(  p_sys->fd_data > 0 )
00649             net_Close( p_sys->fd_data );
00650         p_sys->fd_data = -1;
00651         return VLC_EGENERIC;
00652     }
00653     if(  p_sys->fd_data > 0 )
00654     {
00655         net_Close( p_sys->fd_data );
00656         p_sys->fd_data = -1;
00657         ftp_ReadCommand( p_access, &i_answer, NULL );
00658     }
00659     ftp_ReadCommand( p_access, &i_answer, NULL );
00660 
00661     return VLC_SUCCESS;
00662 }
00663 

Generated on Tue Dec 20 10:14:25 2005 for vlc-0.8.4a by  doxygen 1.4.2