ftp.c
上传用户:riyaled888
上传日期:2009-03-27
资源大小:7338k
文件大小:17k
源码类别:

多媒体

开发平台:

MultiPlatform

  1. /*****************************************************************************
  2.  * ftp.c: FTP input module
  3.  *****************************************************************************
  4.  * Copyright (C) 2001-2004 VideoLAN
  5.  * $Id: ftp.c 8606 2004-08-31 18:32:54Z hartman $
  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.  * Preamble
  25.  *****************************************************************************/
  26. #include <stdlib.h>
  27. #include <vlc/vlc.h>
  28. #include <vlc/input.h>
  29. #include "network.h"
  30. /*****************************************************************************
  31.  * Module descriptor
  32.  *****************************************************************************/
  33. static int     Open ( vlc_object_t * );
  34. static void    Close( vlc_object_t * );
  35. #define CACHING_TEXT N_("Caching value in ms")
  36. #define CACHING_LONGTEXT N_( 
  37.     "Allows you to modify the default caching value for FTP streams. This " 
  38.     "value should be set in millisecond units." )
  39. #define USER_TEXT N_("FTP user name")
  40. #define USER_LONGTEXT N_("Allows you to modify the user name that will " 
  41.     "be used for the connection.")
  42. #define PASS_TEXT N_("FTP password")
  43. #define PASS_LONGTEXT N_("Allows you to modify the password that will be " 
  44.     "used for the connection.")
  45. #define ACCOUNT_TEXT N_("FTP account")
  46. #define ACCOUNT_LONGTEXT N_("Allows you to modify the account that will be " 
  47.     "used for the connection.")
  48. vlc_module_begin();
  49.     set_description( _("FTP input") );
  50.     set_capability( "access2", 0 );
  51.     add_integer( "ftp-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL,
  52.                  CACHING_TEXT, CACHING_LONGTEXT, VLC_TRUE );
  53.     add_string( "ftp-user", "anonymous", NULL, USER_TEXT, USER_LONGTEXT,
  54.                 VLC_FALSE );
  55.     add_string( "ftp-pwd", "anonymous@dummy.org", NULL, PASS_TEXT,
  56.                 PASS_LONGTEXT, VLC_FALSE );
  57.     add_string( "ftp-account", "anonymous", NULL, ACCOUNT_TEXT,
  58.                 ACCOUNT_LONGTEXT, VLC_FALSE );
  59.     add_shortcut( "ftp" );
  60.     set_callbacks( Open, Close );
  61. vlc_module_end();
  62. /*****************************************************************************
  63.  * Local prototypes
  64.  *****************************************************************************/
  65. static int Read( access_t *, uint8_t *, int );
  66. static int Seek( access_t *, int64_t );
  67. static int Control( access_t *, int, va_list );
  68. struct access_sys_t
  69. {
  70.     vlc_url_t url;
  71.     int       fd_cmd;
  72.     int       fd_data;
  73. };
  74. static int  ftp_SendCommand( access_t *, char *, ... );
  75. static int  ftp_ReadCommand( access_t *, int *, char ** );
  76. static int  ftp_StartStream( access_t *, int64_t );
  77. static int  ftp_StopStream ( access_t *);
  78. /****************************************************************************
  79.  * Open: connect to ftp server and ask for file
  80.  ****************************************************************************/
  81. static int Open( vlc_object_t *p_this )
  82. {
  83.     access_t     *p_access = (access_t*)p_this;
  84.     access_sys_t *p_sys;
  85.     char         *psz;
  86.     int          i_answer;
  87.     char         *psz_arg;
  88.     /* Init p_access */
  89.     p_access->pf_read = Read;
  90.     p_access->pf_block = NULL;
  91.     p_access->pf_seek = Seek;
  92.     p_access->pf_control = Control;
  93.     p_access->info.i_update = 0;
  94.     p_access->info.i_size = 0;
  95.     p_access->info.i_pos = 0;
  96.     p_access->info.b_eof = VLC_FALSE;
  97.     p_access->info.i_title = 0;
  98.     p_access->info.i_seekpoint = 0;
  99.     p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) );
  100.     memset( p_sys, 0, sizeof( access_sys_t ) );
  101.     p_sys->fd_cmd = -1;
  102.     p_sys->fd_data = -1;
  103.     /* *** Parse URL and get server addr/port and path *** */
  104.     psz = p_access->psz_path;
  105.     while( *psz == '/' )
  106.     {
  107.         psz++;
  108.     }
  109.     vlc_UrlParse( &p_sys->url, psz, 0 );
  110.     if( p_sys->url.psz_host == NULL || *p_sys->url.psz_host == '' )
  111.     {
  112.         msg_Err( p_access, "invalid server name" );
  113.         goto exit_error;
  114.     }
  115.     if( p_sys->url.i_port <= 0 )
  116.     {
  117.         p_sys->url.i_port = 21; /* default port */
  118.     }
  119.     /* *** Open a TCP connection with server *** */
  120.     msg_Dbg( p_access, "waiting for connection..." );
  121.     p_sys->fd_cmd = net_OpenTCP( p_access, p_sys->url.psz_host,
  122.                                  p_sys->url.i_port );
  123.     if( p_sys->fd_cmd < 0 )
  124.     {
  125.         msg_Err( p_access, "failed to connect with server" );
  126.         goto exit_error;
  127.     }
  128.     for( ;; )
  129.     {
  130.         if( ftp_ReadCommand( p_access, &i_answer, NULL ) != 1 )
  131.         {
  132.             break;
  133.         }
  134.     }
  135.     if( i_answer / 100 != 2 )
  136.     {
  137.         msg_Err( p_access, "connection rejected" );
  138.         goto exit_error;
  139.     }
  140.     msg_Dbg( p_access, "connection accepted (%d)", i_answer );
  141.     psz = var_CreateGetString( p_access, "ftp-user" );
  142.     if( ftp_SendCommand( p_access, "USER %s", psz ) < 0 ||
  143.         ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
  144.     {
  145.         free( psz );
  146.         goto exit_error;
  147.     }
  148.     free( psz );
  149.     switch( i_answer / 100 )
  150.     {
  151.         case 2:
  152.             msg_Dbg( p_access, "user accepted" );
  153.             break;
  154.         case 3:
  155.             msg_Dbg( p_access, "password needed" );
  156.             psz = var_CreateGetString( p_access, "ftp-pwd" );
  157.             if( ftp_SendCommand( p_access, "PASS %s", psz ) < 0 ||
  158.                 ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
  159.             {
  160.                 free( psz );
  161.                 goto exit_error;
  162.             }
  163.             free( psz );
  164.             switch( i_answer / 100 )
  165.             {
  166.                 case 2:
  167.                     msg_Dbg( p_access, "password accepted" );
  168.                     break;
  169.                 case 3:
  170.                     msg_Dbg( p_access, "account needed" );
  171.                     psz = var_CreateGetString( p_access, "ftp-account" );
  172.                     if( ftp_SendCommand( p_access, "ACCT %s",
  173.                                          psz ) < 0 ||
  174.                         ftp_ReadCommand( p_access, &i_answer, NULL ) < 0 )
  175.                     {
  176.                         free( psz );
  177.                         goto exit_error;
  178.                     }
  179.                     free( psz );
  180.                     if( i_answer / 100 != 2 )
  181.                     {
  182.                         msg_Err( p_access, "account rejected" );
  183.                         goto exit_error;
  184.                     }
  185.                     msg_Dbg( p_access, "account accepted" );
  186.                     break;
  187.                 default:
  188.                     msg_Err( p_access, "password rejected" );
  189.                     goto exit_error;
  190.             }
  191.             break;
  192.         default:
  193.             msg_Err( p_access, "user rejected" );
  194.             goto exit_error;
  195.     }
  196.     /* binary mode */
  197.     if( ftp_SendCommand( p_access, "TYPE I" ) < 0 ||
  198.         ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 )
  199.     {
  200.         msg_Err( p_access, "cannot set binary transfer mode" );
  201.         goto exit_error;
  202.     }
  203.     /* get size */
  204.     if( ftp_SendCommand( p_access, "SIZE %s", p_sys->url.psz_path ) < 0 ||
  205.         ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 )
  206.     {
  207.         msg_Err( p_access, "cannot get file size" );
  208.         goto exit_error;
  209.     }
  210.     p_access->info.i_size = atoll( &psz_arg[4] );
  211.     free( psz_arg );
  212.     msg_Dbg( p_access, "file size: "I64Fd, p_access->info.i_size );
  213.     /* Start the 'stream' */
  214.     if( ftp_StartStream( p_access, 0 ) < 0 )
  215.     {
  216.         msg_Err( p_access, "cannot retrieve file" );
  217.         goto exit_error;
  218.     }
  219.     /* Update default_pts to a suitable value for ftp access */
  220.     var_Create( p_access, "ftp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
  221.     return VLC_SUCCESS;
  222. exit_error:
  223.     if( p_sys->fd_cmd > 0 )
  224.     {
  225.         net_Close( p_sys->fd_cmd );
  226.     }
  227.     vlc_UrlClean( &p_sys->url );
  228.     free( p_sys );
  229.     return VLC_EGENERIC;
  230. }
  231. /*****************************************************************************
  232.  * Close: free unused data structures
  233.  *****************************************************************************/
  234. static void Close( vlc_object_t *p_this )
  235. {
  236.     access_t      *p_access = (access_t*)p_this;
  237.     access_sys_t  *p_sys = p_access->p_sys;
  238.     msg_Dbg( p_access, "stopping stream" );
  239.     ftp_StopStream( p_access );
  240.     if( ftp_SendCommand( p_access, "QUIT" ) < 0 )
  241.     {
  242.         msg_Warn( p_access, "cannot quit" );
  243.     }
  244.     else
  245.     {
  246.         ftp_ReadCommand( p_access, NULL, NULL );
  247.     }
  248.     net_Close( p_sys->fd_cmd );
  249.     /* free memory */
  250.     vlc_UrlClean( &p_sys->url );
  251.     free( p_sys );
  252. }
  253. /*****************************************************************************
  254.  * Seek: try to go at the right place
  255.  *****************************************************************************/
  256. static int Seek( access_t *p_access, int64_t i_pos )
  257. {
  258.     if( i_pos < 0 )
  259.     {
  260.         return VLC_EGENERIC;
  261.     }
  262.     msg_Dbg( p_access, "seeking to "I64Fd, i_pos );
  263.     ftp_StopStream( p_access );
  264.     if( ftp_StartStream( p_access, i_pos ) < 0 )
  265.     {
  266.         p_access->info.b_eof = VLC_TRUE;
  267.         return VLC_EGENERIC;
  268.     }
  269.     p_access->info.b_eof = VLC_FALSE;
  270.     p_access->info.i_pos = i_pos;
  271.     return VLC_SUCCESS;
  272. }
  273. /*****************************************************************************
  274.  * Read:
  275.  *****************************************************************************/
  276. static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
  277. {
  278.     access_sys_t *p_sys = p_access->p_sys;
  279.     int i_read;
  280.     if( p_access->info.b_eof )
  281.         return 0;
  282.     i_read = net_Read( p_access, p_sys->fd_data, p_buffer, i_len, VLC_FALSE );
  283.     if( i_read == 0 )
  284.         p_access->info.b_eof = VLC_TRUE;
  285.     else if( i_read > 0 )
  286.         p_access->info.i_pos += i_read;
  287.     return i_read;
  288. }
  289. /*****************************************************************************
  290.  * Control:
  291.  *****************************************************************************/
  292. static int Control( access_t *p_access, int i_query, va_list args )
  293. {
  294.     vlc_bool_t   *pb_bool;
  295.     int          *pi_int;
  296.     int64_t      *pi_64;
  297.     vlc_value_t  val;
  298.     switch( i_query )
  299.     {
  300.         /* */
  301.         case ACCESS_CAN_SEEK:
  302.             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
  303.             *pb_bool = VLC_TRUE;
  304.             break;
  305.         case ACCESS_CAN_FASTSEEK:
  306.             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
  307.             *pb_bool = VLC_FALSE;
  308.             break;
  309.         case ACCESS_CAN_PAUSE:
  310.             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
  311.             *pb_bool = VLC_TRUE;    /* FIXME */
  312.             break;
  313.         case ACCESS_CAN_CONTROL_PACE:
  314.             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
  315.             *pb_bool = VLC_TRUE;    /* FIXME */
  316.             break;
  317.         /* */
  318.         case ACCESS_GET_MTU:
  319.             pi_int = (int*)va_arg( args, int * );
  320.             *pi_int = 0;
  321.             break;
  322.         case ACCESS_GET_PTS_DELAY:
  323.             pi_64 = (int64_t*)va_arg( args, int64_t * );
  324.             var_Get( p_access, "ftp-caching", &val );
  325.             *pi_64 = (int64_t)var_GetInteger( p_access, "ftp-caching" ) * I64C(1000);
  326.             break;
  327.         /* */
  328.         case ACCESS_SET_PAUSE_STATE:
  329.             /* Nothing to do */
  330.             break;
  331.         case ACCESS_GET_TITLE_INFO:
  332.         case ACCESS_SET_TITLE:
  333.         case ACCESS_SET_SEEKPOINT:
  334.         case ACCESS_SET_PRIVATE_ID_STATE:
  335.             return VLC_EGENERIC;
  336.         default:
  337.             msg_Warn( p_access, "unimplemented query in control" );
  338.             return VLC_EGENERIC;
  339.     }
  340.     return VLC_SUCCESS;
  341. }
  342. /*****************************************************************************
  343.  * ftp_*:
  344.  *****************************************************************************/
  345. static int ftp_SendCommand( access_t *p_access, char *psz_fmt, ... )
  346. {
  347.     access_sys_t *p_sys = p_access->p_sys;
  348.     va_list      args;
  349.     char         *psz_cmd;
  350.     int          i_ret;
  351.     va_start( args, psz_fmt );
  352.     vasprintf( &psz_cmd, psz_fmt, args );
  353.     va_end( args );
  354.     msg_Dbg( p_access, "ftp_SendCommand:"%s"", psz_cmd);
  355.     if( ( i_ret = net_Printf( VLC_OBJECT(p_access), p_sys->fd_cmd,
  356.                               "%s", psz_cmd ) ) > 0 )
  357.     {
  358.         i_ret = net_Printf( VLC_OBJECT(p_access), p_sys->fd_cmd, "rn" );
  359.     }
  360.     if( i_ret < 0 )
  361.     {
  362.         msg_Err( p_access, "failed to send command" );
  363.         return VLC_EGENERIC;
  364.     }
  365.     return VLC_SUCCESS;
  366. }
  367. /* TODO support this s**t :
  368.  RFC 959 allows the client to send certain TELNET strings at any moment,
  369.  even in the middle of a request:
  370.  * 377377.
  371.  * 377376x where x is one byte.
  372.  * 377375x where x is one byte. The server is obliged to send 377374x
  373.  *                                immediately after reading x.
  374.  * 377374x where x is one byte.
  375.  * 377373x where x is one byte. The server is obliged to send 377376x
  376.  *                                immediately after reading x.
  377.  * 377x for any other byte x.
  378.  These strings are not part of the requests, except in the case 377377,
  379.  where the request contains one 377. */
  380. static int ftp_ReadCommand( access_t *p_access,
  381.                             int *pi_answer, char **ppsz_answer )
  382. {
  383.     access_sys_t *p_sys = p_access->p_sys;
  384.     char         *psz_line;
  385.     int          i_answer;
  386.     psz_line = net_Gets( p_access, p_sys->fd_cmd );
  387.     msg_Dbg( p_access, "answer=%s", psz_line );
  388.     if( psz_line == NULL || strlen( psz_line ) < 3 )
  389.     {
  390.         msg_Err( p_access, "cannot get answer" );
  391.         if( psz_line ) free( psz_line );
  392.         if( pi_answer ) *pi_answer    = 500;
  393.         if( ppsz_answer ) *ppsz_answer  = NULL;
  394.         return -1;
  395.     }
  396.     if( psz_line[3] == '-' )    /* Multiple response */
  397.     {
  398.         char end[4];
  399.         memcpy( end, psz_line, 3 );
  400.         end[3] = ' ';
  401.         for( ;; )
  402.         {
  403.             char *psz_tmp = net_Gets( p_access, p_sys->fd_cmd );
  404.             if( psz_tmp == NULL )   /* Error */
  405.                 break;
  406.             if( !strncmp( psz_tmp, end, 4 ) )
  407.             {
  408.                 free( psz_tmp );
  409.                 break;
  410.             }
  411.             free( psz_tmp );
  412.         }
  413.     }
  414.     i_answer = atoi( psz_line );
  415.     if( pi_answer ) *pi_answer = i_answer;
  416.     if( ppsz_answer )
  417.     {
  418.         *ppsz_answer = psz_line;
  419.     }
  420.     else
  421.     {
  422.         free( psz_line );
  423.     }
  424.     return( i_answer / 100 );
  425. }
  426. static int ftp_StartStream( access_t *p_access, off_t i_start )
  427. {
  428.     access_sys_t *p_sys = p_access->p_sys;
  429.     char psz_ip[1000];
  430.     int  i_answer;
  431.     char *psz_arg, *psz_parser;
  432.     int  a1,a2,a3,a4;
  433.     int  p1,p2;
  434.     int  i_port;
  435.     if( ftp_SendCommand( p_access, "PASV" ) < 0 ||
  436.         ftp_ReadCommand( p_access, &i_answer, &psz_arg ) != 2 )
  437.     {
  438.         msg_Err( p_access, "cannot set passive transfer mode" );
  439.         return VLC_EGENERIC;
  440.     }
  441.     psz_parser = strchr( psz_arg, '(' );
  442.     if( !psz_parser ||
  443.         sscanf( psz_parser, "(%d,%d,%d,%d,%d,%d", &a1, &a2, &a3,
  444.                 &a4, &p1, &p2 ) < 6 )
  445.     {
  446.         free( psz_arg );
  447.         msg_Err( p_access, "cannot get ip/port for passive transfer mode" );
  448.         return VLC_EGENERIC;
  449.     }
  450.     free( psz_arg );
  451.     sprintf( psz_ip, "%d.%d.%d.%d", a1, a2, a3, a4 );
  452.     i_port = p1 * 256 + p2;
  453.     msg_Dbg( p_access, "ip:%s port:%d", psz_ip, i_port );
  454.     if( ftp_SendCommand( p_access, "TYPE I" ) < 0 ||
  455.         ftp_ReadCommand( p_access, &i_answer, NULL ) != 2 )
  456.     {
  457.         msg_Err( p_access, "cannot set binary transfer mode" );
  458.         return VLC_EGENERIC;
  459.     }
  460.     if( i_start > 0 )
  461.     {
  462.         if( ftp_SendCommand( p_access, "REST "I64Fu, i_start ) < 0 ||
  463.             ftp_ReadCommand( p_access, &i_answer, NULL ) > 3 )
  464.         {
  465.             msg_Err( p_access, "cannot set restart point" );
  466.             return VLC_EGENERIC;
  467.         }
  468.     }
  469.     msg_Dbg( p_access, "waiting for data connection..." );
  470.     p_sys->fd_data = net_OpenTCP( p_access, psz_ip, i_port );
  471.     if( p_sys->fd_data < 0 )
  472.     {
  473.         msg_Err( p_access, "failed to connect with server" );
  474.         return VLC_EGENERIC;
  475.     }
  476.     msg_Dbg( p_access, "connection with "%s:%d" successful",
  477.              psz_ip, i_port );
  478.     /* "1xx" message */
  479.     if( ftp_SendCommand( p_access, "RETR %s", p_sys->url.psz_path ) < 0 ||
  480.         ftp_ReadCommand( p_access, &i_answer, NULL ) > 2 )
  481.     {
  482.         msg_Err( p_access, "cannot retreive file" );
  483.         return VLC_EGENERIC;
  484.     }
  485.     return VLC_SUCCESS;
  486. }
  487. static int ftp_StopStream ( access_t *p_access )
  488. {
  489.     access_sys_t *p_sys = p_access->p_sys;
  490.     int i_answer;
  491.     if( ftp_SendCommand( p_access, "ABOR" ) < 0 )
  492.     {
  493.         msg_Warn( p_access, "cannot abord file" );
  494.         if(  p_sys->fd_data > 0 )
  495.             net_Close( p_sys->fd_data );
  496.         p_sys->fd_data = -1;
  497.         return VLC_EGENERIC;
  498.     }
  499.     if(  p_sys->fd_data > 0 )
  500.     {
  501.         net_Close( p_sys->fd_data );
  502.         p_sys->fd_data = -1;
  503.         ftp_ReadCommand( p_access, &i_answer, NULL );
  504.     }
  505.     ftp_ReadCommand( p_access, &i_answer, NULL );
  506.     return VLC_SUCCESS;
  507. }