ftp.c
上传用户:kjfoods
上传日期:2020-07-06
资源大小:29949k
文件大小:25k
源码类别:

midi

开发平台:

Unix_Linux

  1. /*****************************************************************************
  2.  * ftp.c: FTP input module
  3.  *****************************************************************************
  4.  * Copyright (C) 2001-2006 the VideoLAN team
  5.  * Copyright © 2006 Rémi Denis-Courmont
  6.  * $Id: dffea0dd7a0cad40ab772d5cbb55146d72aefe40 $
  7.  *
  8.  * Authors: Laurent Aimar <fenrir@via.ecp.fr> - original code
  9.  *          Rémi Denis-Courmont <rem # videolan.org> - EPSV support
  10.  *
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
  24.  *****************************************************************************/
  25. /*****************************************************************************
  26.  * Preamble
  27.  *****************************************************************************/
  28. #ifdef HAVE_CONFIG_H
  29. # include "config.h"
  30. #endif
  31. #include <vlc_common.h>
  32. #include <vlc_plugin.h>
  33. #include <assert.h>
  34. #include <vlc_access.h>
  35. #include <vlc_dialog.h>
  36. #include <vlc_network.h>
  37. #include <vlc_url.h>
  38. #include <vlc_sout.h>
  39. #ifndef IPPORT_FTP
  40. # define IPPORT_FTP 21u
  41. #endif
  42. /*****************************************************************************
  43.  * Module descriptor
  44.  *****************************************************************************/
  45. static int   InOpen ( vlc_object_t * );
  46. static void  InClose( vlc_object_t * );
  47. static int  OutOpen ( vlc_object_t * );
  48. static void OutClose( vlc_object_t * );
  49. #define CACHING_TEXT N_("Caching value in ms")
  50. #define CACHING_LONGTEXT N_( 
  51.     "Caching value for FTP streams. This " 
  52.     "value should be set in milliseconds." )
  53. #define USER_TEXT N_("FTP user name")
  54. #define USER_LONGTEXT N_("User name that will " 
  55.     "be used for the connection.")
  56. #define PASS_TEXT N_("FTP password")
  57. #define PASS_LONGTEXT N_("Password that will be " 
  58.     "used for the connection.")
  59. #define ACCOUNT_TEXT N_("FTP account")
  60. #define ACCOUNT_LONGTEXT N_("Account that will be " 
  61.     "used for the connection.")
  62. vlc_module_begin ()
  63.     set_shortname( "FTP" )
  64.     set_description( N_("FTP input") )
  65.     set_capability( "access", 0 )
  66.     set_category( CAT_INPUT )
  67.     set_subcategory( SUBCAT_INPUT_ACCESS )
  68.     add_integer( "ftp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL,
  69.                  CACHING_TEXT, CACHING_LONGTEXT, true )
  70.         change_safe()
  71.     add_string( "ftp-user", "anonymous", NULL, USER_TEXT, USER_LONGTEXT,
  72.                 false )
  73.     add_string( "ftp-pwd", "anonymous@example.com", NULL, PASS_TEXT,
  74.                 PASS_LONGTEXT, false )
  75.     add_string( "ftp-account", "anonymous", NULL, ACCOUNT_TEXT,
  76.                 ACCOUNT_LONGTEXT, false )
  77.     add_shortcut( "ftp" )
  78.     set_callbacks( InOpen, InClose )
  79.     add_submodule ()
  80.         set_shortname( "FTP" )
  81.         set_description( N_("FTP upload output") )
  82.         set_capability( "sout access", 0 )
  83.         set_category( CAT_SOUT )
  84.         set_subcategory( SUBCAT_SOUT_ACO )
  85.         add_shortcut( "ftp" )
  86.         set_callbacks( OutOpen, OutClose )
  87. vlc_module_end ()
  88. /*****************************************************************************
  89.  * Local prototypes
  90.  *****************************************************************************/
  91. static ssize_t Read( access_t *, uint8_t *, size_t );
  92. static ssize_t Write( sout_access_out_t *, block_t * );
  93. static int Seek( access_t *, int64_t );
  94. static int OutSeek( sout_access_out_t *, off_t );
  95. static int Control( access_t *, int, va_list );
  96. struct access_sys_t
  97. {
  98.     vlc_url_t  url;
  99.     int        fd_cmd;
  100.     int        fd_data;
  101.     char       sz_epsv_ip[NI_MAXNUMERICHOST];
  102.     bool       out;
  103.     bool       directory;
  104. };
  105. #define GET_OUT_SYS( p_this ) 
  106.     ((access_sys_t *)(((sout_access_out_t *)(p_this))->p_sys))
  107. static int ftp_SendCommand( vlc_object_t *, access_sys_t *, const char *, ... );
  108. static int ftp_ReadCommand( vlc_object_t *, access_sys_t *, int *, char ** );
  109. static int ftp_StartStream( vlc_object_t *, access_sys_t *, int64_t );
  110. static int ftp_StopStream ( vlc_object_t *, access_sys_t * );
  111. static int Login( vlc_object_t *p_access, access_sys_t *p_sys )
  112. {
  113.     int i_answer;
  114.     char *psz;
  115.     /* *** Open a TCP connection with server *** */
  116.     int fd = p_sys->fd_cmd = net_ConnectTCP( p_access, p_sys->url.psz_host,
  117.                                              p_sys->url.i_port );
  118.     if( fd == -1 )
  119.     {
  120.         msg_Err( p_access, "connection failed" );
  121.         dialog_Fatal( p_access, _("Network interaction failed"), "%s",
  122.                         _("VLC could not connect with the given server.") );
  123.         return -1;
  124.     }
  125.     while( ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) == 1 );
  126.     if( i_answer / 100 != 2 )
  127.     {
  128.         msg_Err( p_access, "connection rejected" );
  129.         dialog_Fatal( p_access, _("Network interaction failed"), "%s",
  130.                         _("VLC's connection to the given server was rejected.") );
  131.         return -1;
  132.     }
  133.     msg_Dbg( p_access, "connection accepted (%d)", i_answer );
  134.     if( p_sys->url.psz_username && *p_sys->url.psz_username )
  135.         psz = strdup( p_sys->url.psz_username );
  136.     else
  137.         psz = var_CreateGetString( p_access, "ftp-user" );
  138.     if( !psz )
  139.         return -1;
  140.     if( ftp_SendCommand( p_access, p_sys, "USER %s", psz ) < 0 ||
  141.         ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 )
  142.     {
  143.         free( psz );
  144.         return -1;
  145.     }
  146.     free( psz );
  147.     switch( i_answer / 100 )
  148.     {
  149.         case 2:
  150.             msg_Dbg( p_access, "user accepted" );
  151.             break;
  152.         case 3:
  153.             msg_Dbg( p_access, "password needed" );
  154.             if( p_sys->url.psz_password && *p_sys->url.psz_password )
  155.                 psz = strdup( p_sys->url.psz_password );
  156.             else
  157.                 psz = var_CreateGetString( p_access, "ftp-pwd" );
  158.             if( !psz )
  159.                 return -1;
  160.             if( ftp_SendCommand( p_access, p_sys, "PASS %s", psz ) < 0 ||
  161.                 ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 )
  162.             {
  163.                 free( psz );
  164.                 return -1;
  165.             }
  166.             free( psz );
  167.             switch( i_answer / 100 )
  168.             {
  169.                 case 2:
  170.                     msg_Dbg( p_access, "password accepted" );
  171.                     break;
  172.                 case 3:
  173.                     msg_Dbg( p_access, "account needed" );
  174.                     psz = var_CreateGetString( p_access, "ftp-account" );
  175.                     if( ftp_SendCommand( p_access, p_sys, "ACCT %s",
  176.                                          psz ) < 0 ||
  177.                         ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 )
  178.                     {
  179.                         free( psz );
  180.                         return -1;
  181.                     }
  182.                     free( psz );
  183.                     if( i_answer / 100 != 2 )
  184.                     {
  185.                         msg_Err( p_access, "account rejected" );
  186.                         dialog_Fatal( p_access,
  187.                                       _("Network interaction failed"),
  188.                                       "%s", _("Your account was rejected.") );
  189.                         return -1;
  190.                     }
  191.                     msg_Dbg( p_access, "account accepted" );
  192.                     break;
  193.                 default:
  194.                     msg_Err( p_access, "password rejected" );
  195.                     dialog_Fatal( p_access, _("Network interaction failed"),
  196.                                   "%s",  _("Your password was rejected.") );
  197.                     return -1;
  198.             }
  199.             break;
  200.         default:
  201.             msg_Err( p_access, "user rejected" );
  202.             dialog_Fatal( p_access, _("Network interaction failed"), "%s",
  203.                         _("Your connection attempt to the server was rejected.") );
  204.             return -1;
  205.     }
  206.     return 0;
  207. }
  208. static int Connect( vlc_object_t *p_access, access_sys_t *p_sys )
  209. {
  210.     if( Login( p_access, p_sys ) < 0 )
  211.         return -1;
  212.     /* Extended passive mode */
  213.     if( ftp_SendCommand( p_access, p_sys, "EPSV ALL" ) < 0 )
  214.     {
  215.         msg_Err( p_access, "cannot request extended passive mode" );
  216.         net_Close( p_sys->fd_cmd );
  217.         return -1;
  218.     }
  219.     if( ftp_ReadCommand( p_access, p_sys, NULL, NULL ) == 2 )
  220.     {
  221.         if( net_GetPeerAddress( p_sys->fd_cmd, p_sys->sz_epsv_ip, NULL ) )
  222.         {
  223.             net_Close( p_sys->fd_cmd );
  224.             return -1;
  225.         }
  226.     }
  227.     else
  228.     {
  229.         /* If ESPV ALL fails, we fallback to PASV.
  230.          * We have to restart the connection in case there is a NAT that
  231.          * understands EPSV ALL in the way, and hence won't allow PASV on
  232.          * the initial connection.
  233.          */
  234.         msg_Info( p_access, "FTP Extended passive mode disabled" );
  235.         net_Close( p_sys->fd_cmd );
  236.         if( Login( p_access, p_sys ) )
  237.         {
  238.             net_Close( p_sys->fd_cmd );
  239.             return -1;
  240.         }
  241.     }
  242.     /* check binary mode support */
  243.     if( ftp_SendCommand( p_access, p_sys, "TYPE I" ) < 0 ||
  244.         ftp_ReadCommand( p_access, p_sys, NULL, NULL ) != 2 )
  245.     {
  246.         msg_Err( p_access, "cannot set binary transfer mode" );
  247.         net_Close( p_sys->fd_cmd );
  248.         return -1;
  249.     }
  250.     return 0;
  251. }
  252. static int parseURL( vlc_url_t *url, const char *path )
  253. {
  254.     if( path == NULL )
  255.         return VLC_EGENERIC;
  256.     /* *** Parse URL and get server addr/port and path *** */
  257.     while( *path == '/' )
  258.         path++;
  259.     vlc_UrlParse( url, path, 0 );
  260.     if( url->psz_host == NULL || *url->psz_host == '' )
  261.         return VLC_EGENERIC;
  262.     if( url->i_port <= 0 )
  263.         url->i_port = IPPORT_FTP; /* default port */
  264.     /* FTP URLs are relative to user's default directory (RFC1738 §3.2)
  265.     For absolute path use ftp://foo.bar//usr/local/etc/filename */
  266.     /* FIXME: we should issue a series of CWD, one per slash */
  267.     if( url->psz_path && *url->psz_path == '/' )
  268.         url->psz_path++;
  269.     char *type = strstr( url->psz_path, ";type=" );
  270.     if( type )
  271.     {
  272.         *type = '';
  273.         if( strchr( "iI", type[6] ) == NULL )
  274.             return VLC_EGENERIC; /* ASCII and directory not supported */
  275.     }
  276.     decode_URI( url->psz_path );
  277.     return VLC_SUCCESS;
  278. }
  279. /****************************************************************************
  280.  * Open: connect to ftp server and ask for file
  281.  ****************************************************************************/
  282. static int InOpen( vlc_object_t *p_this )
  283. {
  284.     access_t     *p_access = (access_t*)p_this;
  285.     access_sys_t *p_sys;
  286.     char         *psz_arg;
  287.     /* Init p_access */
  288.     STANDARD_READ_ACCESS_INIT
  289.     p_sys->fd_data = -1;
  290.     p_sys->out = false;
  291.     p_sys->directory = false;
  292.     if( parseURL( &p_sys->url, p_access->psz_path ) )
  293.         goto exit_error;
  294.     if( Connect( p_this, p_sys ) )
  295.         goto exit_error;
  296.     /* get size */
  297.     if( ftp_SendCommand( p_this, p_sys, "SIZE %s", p_sys->url.psz_path
  298.                                                ? p_sys->url.psz_path : "" ) < 0
  299.      || ftp_ReadCommand( p_this, p_sys, NULL, &psz_arg ) != 2 )
  300.     {
  301.         msg_Dbg( p_access, "cannot get file size" );
  302.         msg_Dbg( p_access, "will try to get directory contents" );
  303.         if( ftp_SendCommand( p_this, p_sys, "CWD %s", p_sys->url.psz_path
  304.                              ? p_sys->url.psz_path : "" ) < 0 ||
  305.         ftp_ReadCommand( p_this, p_sys, NULL, &psz_arg ) != 2 )
  306.         {
  307.             msg_Err( p_access, "file or directory doesn't exist" );
  308.             net_Close( p_sys->fd_cmd );
  309.             goto exit_error;
  310.         }
  311.         p_sys->directory = true;
  312.     }
  313.     else
  314.     {
  315.         p_access->info.i_size = atoll( &psz_arg[4] );
  316.         free( psz_arg );
  317.         msg_Dbg( p_access, "file size: %"PRId64, p_access->info.i_size );
  318.     }
  319.     /* Start the 'stream' */
  320.     if( ftp_StartStream( p_this, p_sys, 0 ) < 0 )
  321.     {
  322.         msg_Err( p_access, "cannot retrieve file" );
  323.         net_Close( p_sys->fd_cmd );
  324.         goto exit_error;
  325.     }
  326.     /* Update default_pts to a suitable value for ftp access */
  327.     var_Create( p_access, "ftp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
  328.     return VLC_SUCCESS;
  329. exit_error:
  330.     vlc_UrlClean( &p_sys->url );
  331.     free( p_sys );
  332.     return VLC_EGENERIC;
  333. }
  334. static int OutOpen( vlc_object_t *p_this )
  335. {
  336.     sout_access_out_t *p_access = (sout_access_out_t *)p_this;
  337.     access_sys_t      *p_sys;
  338.     p_sys = calloc( 1, sizeof( *p_sys ) );
  339.     if( !p_sys )
  340.         return VLC_ENOMEM;
  341.     /* Init p_access */
  342.     p_sys->fd_data = -1;
  343.     p_sys->out = true;
  344.     if( parseURL( &p_sys->url, p_access->psz_path ) )
  345.         goto exit_error;
  346.     if( Connect( p_this, p_sys ) )
  347.         goto exit_error;
  348.     /* Start the 'stream' */
  349.     if( ftp_StartStream( p_this, p_sys, 0 ) < 0 )
  350.     {
  351.         msg_Err( p_access, "cannot store file" );
  352.         net_Close( p_sys->fd_cmd );
  353.         goto exit_error;
  354.     }
  355.     p_access->pf_seek = OutSeek;
  356.     p_access->pf_write = Write;
  357.     p_access->p_sys = (void *)p_sys;
  358.     return VLC_SUCCESS;
  359. exit_error:
  360.     vlc_UrlClean( &p_sys->url );
  361.     free( p_sys );
  362.     return VLC_EGENERIC;
  363. }
  364. /*****************************************************************************
  365.  * Close: free unused data structures
  366.  *****************************************************************************/
  367. static void Close( vlc_object_t *p_access, access_sys_t *p_sys )
  368. {
  369.     msg_Dbg( p_access, "stopping stream" );
  370.     ftp_StopStream( p_access, p_sys );
  371.     if( ftp_SendCommand( p_access, p_sys, "QUIT" ) < 0 )
  372.     {
  373.         msg_Warn( p_access, "cannot quit" );
  374.     }
  375.     else
  376.     {
  377.         ftp_ReadCommand( p_access, p_sys, NULL, NULL );
  378.     }
  379.     net_Close( p_sys->fd_cmd );
  380.     /* free memory */
  381.     vlc_UrlClean( &p_sys->url );
  382.     free( p_sys );
  383. }
  384. static void InClose( vlc_object_t *p_this )
  385. {
  386.     Close( p_this, ((access_t *)p_this)->p_sys);
  387. }
  388. static void OutClose( vlc_object_t *p_this )
  389. {
  390.     Close( p_this, GET_OUT_SYS(p_this));
  391. }
  392. /*****************************************************************************
  393.  * Seek: try to go at the right place
  394.  *****************************************************************************/
  395. static int _Seek( vlc_object_t *p_access, access_sys_t *p_sys, int64_t i_pos )
  396. {
  397.     if( i_pos < 0 )
  398.         return VLC_EGENERIC;
  399.     msg_Dbg( p_access, "seeking to %"PRId64, i_pos );
  400.     ftp_StopStream( (vlc_object_t *)p_access, p_sys );
  401.     if( ftp_StartStream( (vlc_object_t *)p_access, p_sys, i_pos ) < 0 )
  402.         return VLC_EGENERIC;
  403.     return VLC_SUCCESS;
  404. }
  405. static int Seek( access_t *p_access, int64_t i_pos )
  406. {
  407.     int val = _Seek( (vlc_object_t *)p_access, p_access->p_sys, i_pos );
  408.     if( val )
  409.         return val;
  410.     p_access->info.b_eof = false;
  411.     p_access->info.i_pos = i_pos;
  412.     return VLC_SUCCESS;
  413. }
  414. static int OutSeek( sout_access_out_t *p_access, off_t i_pos )
  415. {
  416.     return _Seek( (vlc_object_t *)p_access, GET_OUT_SYS( p_access ), i_pos);
  417. }
  418. /*****************************************************************************
  419.  * Read:
  420.  *****************************************************************************/
  421. static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
  422. {
  423.     access_sys_t *p_sys = p_access->p_sys;
  424.     assert( p_sys->fd_data != -1 );
  425.     assert( !p_sys->out );
  426.     if( p_access->info.b_eof )
  427.         return 0;
  428.     if( p_sys->directory )
  429.     {
  430.         char *psz_line = net_Gets( p_access, p_sys->fd_data, NULL );
  431.         if( !psz_line )
  432.         {
  433.             p_access->info.b_eof = true;
  434.             return 0;
  435.         }
  436.         else
  437.         {
  438.             snprintf( (char*)p_buffer, i_len, "ftp://%s:%d/%s/%sn",
  439.                       p_sys->url.psz_host, p_sys->url.i_port,
  440.                       p_sys->url.psz_path, psz_line );
  441.             free( psz_line );
  442.             return strlen( (const char *)p_buffer );
  443.         }
  444.     }
  445.     else
  446.     {
  447.         int i_read = net_Read( p_access, p_sys->fd_data, NULL,
  448.                                p_buffer, i_len, false );
  449.         if( i_read == 0 )
  450.             p_access->info.b_eof = true;
  451.         else if( i_read > 0 )
  452.             p_access->info.i_pos += i_read;
  453.         return i_read;
  454.     }
  455. }
  456. /*****************************************************************************
  457.  * Write:
  458.  *****************************************************************************/
  459. static ssize_t Write( sout_access_out_t *p_access, block_t *p_buffer )
  460. {
  461.     access_sys_t *p_sys = GET_OUT_SYS(p_access);
  462.     size_t i_write = 0;
  463.     assert( p_sys->fd_data != -1 );
  464.     while( p_buffer != NULL )
  465.     {
  466.         block_t *p_next = p_buffer->p_next;;
  467.         i_write += net_Write( p_access, p_sys->fd_data, NULL,
  468.                               p_buffer->p_buffer, p_buffer->i_buffer );
  469.         block_Release( p_buffer );
  470.         p_buffer = p_next;
  471.     }
  472.     return i_write;
  473. }
  474. /*****************************************************************************
  475.  * Control:
  476.  *****************************************************************************/
  477. static int Control( access_t *p_access, int i_query, va_list args )
  478. {
  479.     bool    *pb_bool;
  480.     int64_t *pi_64;
  481.     switch( i_query )
  482.     {
  483.         /* */
  484.         case ACCESS_CAN_SEEK:
  485.             pb_bool = (bool*)va_arg( args, bool* );
  486.             *pb_bool = true;
  487.             break;
  488.         case ACCESS_CAN_FASTSEEK:
  489.             pb_bool = (bool*)va_arg( args, bool* );
  490.             *pb_bool = false;
  491.             break;
  492.         case ACCESS_CAN_PAUSE:
  493.             pb_bool = (bool*)va_arg( args, bool* );
  494.             *pb_bool = true;    /* FIXME */
  495.             break;
  496.         case ACCESS_CAN_CONTROL_PACE:
  497.             pb_bool = (bool*)va_arg( args, bool* );
  498.             *pb_bool = true;    /* FIXME */
  499.             break;
  500.         /* */
  501.         case ACCESS_GET_PTS_DELAY:
  502.             pi_64 = (int64_t*)va_arg( args, int64_t * );
  503.             *pi_64 = (int64_t)var_GetInteger( p_access, "ftp-caching" ) * INT64_C(1000);
  504.             break;
  505.         /* */
  506.         case ACCESS_SET_PAUSE_STATE:
  507.             pb_bool = (bool*)va_arg( args, bool* );
  508.             if ( !pb_bool )
  509.               return Seek( p_access, p_access->info.i_pos );
  510.             break;
  511.         case ACCESS_GET_TITLE_INFO:
  512.         case ACCESS_SET_TITLE:
  513.         case ACCESS_SET_SEEKPOINT:
  514.         case ACCESS_SET_PRIVATE_ID_STATE:
  515.         case ACCESS_GET_CONTENT_TYPE:
  516.         case ACCESS_GET_META:
  517.             return VLC_EGENERIC;
  518.         default:
  519.             msg_Warn( p_access, "unimplemented query in control: %d", i_query);
  520.             return VLC_EGENERIC;
  521.     }
  522.     return VLC_SUCCESS;
  523. }
  524. /*****************************************************************************
  525.  * ftp_*:
  526.  *****************************************************************************/
  527. static int ftp_SendCommand( vlc_object_t *p_access, access_sys_t *p_sys,
  528.                             const char *psz_fmt, ... )
  529. {
  530.     va_list      args;
  531.     char         *psz_cmd;
  532.     va_start( args, psz_fmt );
  533.     if( vasprintf( &psz_cmd, psz_fmt, args ) == -1 )
  534.         return VLC_EGENERIC;
  535.     va_end( args );
  536.     msg_Dbg( p_access, "ftp_SendCommand:"%s"", psz_cmd);
  537.     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd_cmd, NULL, "%srn",
  538.                     psz_cmd ) < 0 )
  539.     {
  540.         msg_Err( p_access, "failed to send command" );
  541.         return VLC_EGENERIC;
  542.     }
  543.     return VLC_SUCCESS;
  544. }
  545. /* TODO support this s**t :
  546.  RFC 959 allows the client to send certain TELNET strings at any moment,
  547.  even in the middle of a request:
  548.  * 377377.
  549.  * 377376x where x is one byte.
  550.  * 377375x where x is one byte. The server is obliged to send 377374x
  551.  *                                immediately after reading x.
  552.  * 377374x where x is one byte.
  553.  * 377373x where x is one byte. The server is obliged to send 377376x
  554.  *                                immediately after reading x.
  555.  * 377x for any other byte x.
  556.  These strings are not part of the requests, except in the case 377377,
  557.  where the request contains one 377. */
  558. static int ftp_ReadCommand( vlc_object_t *p_access, access_sys_t *p_sys,
  559.                             int *pi_answer, char **ppsz_answer )
  560. {
  561.     char         *psz_line;
  562.     int          i_answer;
  563.     psz_line = net_Gets( p_access, p_sys->fd_cmd, NULL );
  564.     if( psz_line == NULL || strlen( psz_line ) < 3 )
  565.     {
  566.         msg_Err( p_access, "cannot get answer" );
  567.         free( psz_line );
  568.         if( pi_answer ) *pi_answer    = 500;
  569.         if( ppsz_answer ) *ppsz_answer  = NULL;
  570.         return -1;
  571.     }
  572.     msg_Dbg( p_access, "answer=%s", psz_line );
  573.     if( psz_line[3] == '-' )    /* Multiple response */
  574.     {
  575.         char end[4];
  576.         memcpy( end, psz_line, 3 );
  577.         end[3] = ' ';
  578.         for( ;; )
  579.         {
  580.             char *psz_tmp = net_Gets( p_access, p_sys->fd_cmd, NULL );
  581.             if( psz_tmp == NULL )   /* Error */
  582.                 break;
  583.             if( !strncmp( psz_tmp, end, 4 ) )
  584.             {
  585.                 free( psz_tmp );
  586.                 break;
  587.             }
  588.             free( psz_tmp );
  589.         }
  590.     }
  591.     i_answer = atoi( psz_line );
  592.     if( pi_answer ) *pi_answer = i_answer;
  593.     if( ppsz_answer )
  594.     {
  595.         *ppsz_answer = psz_line;
  596.     }
  597.     else
  598.     {
  599.         free( psz_line );
  600.     }
  601.     return( i_answer / 100 );
  602. }
  603. static int ftp_StartStream( vlc_object_t *p_access, access_sys_t *p_sys,
  604.                             int64_t i_start )
  605. {
  606.     char psz_ipv4[16], *psz_ip = p_sys->sz_epsv_ip;
  607.     int  i_answer;
  608.     char *psz_arg, *psz_parser;
  609.     int  i_port;
  610.     assert( p_sys->fd_data == -1 );
  611.     if( ( ftp_SendCommand( p_access, p_sys, *psz_ip ? "EPSV" : "PASV" ) < 0 )
  612.      || ( ftp_ReadCommand( p_access, p_sys, &i_answer, &psz_arg ) != 2 ) )
  613.     {
  614.         msg_Err( p_access, "cannot set passive mode" );
  615.         return VLC_EGENERIC;
  616.     }
  617.     psz_parser = strchr( psz_arg, '(' );
  618.     if( psz_parser == NULL )
  619.     {
  620.         free( psz_arg );
  621.         msg_Err( p_access, "cannot parse passive mode response" );
  622.         return VLC_EGENERIC;
  623.     }
  624.     if( *psz_ip )
  625.     {
  626.         char psz_fmt[7] = "(|||%u";
  627.         psz_fmt[1] = psz_fmt[2] = psz_fmt[3] = psz_parser[1];
  628.         if( sscanf( psz_parser, psz_fmt, &i_port ) < 1 )
  629.         {
  630.             free( psz_arg );
  631.             msg_Err( p_access, "cannot parse passive mode response" );
  632.             return VLC_EGENERIC;
  633.         }
  634.     }
  635.     else
  636.     {
  637.         unsigned a1, a2, a3, a4, p1, p2;
  638.         if( ( sscanf( psz_parser, "(%u,%u,%u,%u,%u,%u", &a1, &a2, &a3, &a4,
  639.                       &p1, &p2 ) < 6 ) || ( a1 > 255 ) || ( a2 > 255 )
  640.          || ( a3 > 255 ) || ( a4 > 255 ) || ( p1 > 255 ) || ( p2 > 255 ) )
  641.         {
  642.             free( psz_arg );
  643.             msg_Err( p_access, "cannot parse passive mode response" );
  644.             return VLC_EGENERIC;
  645.         }
  646.         sprintf( psz_ipv4, "%u.%u.%u.%u", a1, a2, a3, a4 );
  647.         psz_ip = psz_ipv4;
  648.         i_port = (p1 << 8) | p2;
  649.     }
  650.     free( psz_arg );
  651.     msg_Dbg( p_access, "ip:%s port:%d", psz_ip, i_port );
  652.     if( ftp_SendCommand( p_access, p_sys, "TYPE I" ) < 0 ||
  653.         ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) != 2 )
  654.     {
  655.         msg_Err( p_access, "cannot set binary transfer mode" );
  656.         return VLC_EGENERIC;
  657.     }
  658.     if( i_start > 0 )
  659.     {
  660.         if( ftp_SendCommand( p_access, p_sys, "REST %"PRIu64, i_start ) < 0 ||
  661.             ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) > 3 )
  662.         {
  663.             msg_Err( p_access, "cannot set restart offset" );
  664.             return VLC_EGENERIC;
  665.         }
  666.     }
  667.     msg_Dbg( p_access, "waiting for data connection..." );
  668.     p_sys->fd_data = net_ConnectTCP( p_access, psz_ip, i_port );
  669.     if( p_sys->fd_data < 0 )
  670.     {
  671.         msg_Err( p_access, "failed to connect with server" );
  672.         return VLC_EGENERIC;
  673.     }
  674.     msg_Dbg( p_access, "connection with "%s:%d" successful",
  675.              psz_ip, i_port );
  676.     if( p_sys->directory )
  677.     {
  678.         if( ftp_SendCommand( p_access, p_sys, "NLST" ) < 0 ||
  679.             ftp_ReadCommand( p_access, p_sys, NULL, &psz_arg ) > 2 )
  680.         {
  681.             msg_Err( p_access, "cannot list directory contents" );
  682.         return VLC_EGENERIC;
  683.         }
  684.     }
  685.     else
  686.     {
  687.         /* "1xx" message */
  688.         if( ftp_SendCommand( p_access, p_sys, "%s %s",
  689.                              p_sys->out ? "STOR" : "RETR",
  690.                            p_sys->url.psz_path ? p_sys->url.psz_path : "" ) < 0
  691.          || ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) > 2 )
  692.         {
  693.             msg_Err( p_access, "cannot retrieve file" );
  694.             return VLC_EGENERIC;
  695.         }
  696.     }
  697.     shutdown( p_sys->fd_data, p_sys->out ? SHUT_RD : SHUT_WR );
  698.     return VLC_SUCCESS;
  699. }
  700. static int ftp_StopStream ( vlc_object_t *p_access, access_sys_t *p_sys )
  701. {
  702.     if( ftp_SendCommand( p_access, p_sys, "ABOR" ) < 0 )
  703.     {
  704.         msg_Warn( p_access, "cannot abort file" );
  705.         if(  p_sys->fd_data > 0 )
  706.             net_Close( p_sys->fd_data );
  707.         p_sys->fd_data = -1;
  708.         return VLC_EGENERIC;
  709.     }
  710.     if( p_sys->fd_data != -1 )
  711.     {
  712.         net_Close( p_sys->fd_data );
  713.         p_sys->fd_data = -1;
  714.         /* Read the final response from RETR/STOR, i.e. 426 or 226 */
  715.         ftp_ReadCommand( p_access, p_sys, NULL, NULL );
  716.     }
  717.     /* Read the response from ABOR, i.e. 226 or 225 */
  718.     ftp_ReadCommand( p_access, p_sys, NULL, NULL );
  719.     return VLC_SUCCESS;
  720. }