]> git.sesse.net Git - vlc/blob - modules/access/ftp.c
Oups
[vlc] / modules / access / ftp.c
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$
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 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>
30 #include <assert.h>
31
32 #include <vlc/vlc.h>
33 #include <vlc/input.h>
34 #include <vlc_interaction.h>
35
36 #include "network.h"
37 #include "vlc_url.h"
38 #include "stream_output.h"
39
40 /*****************************************************************************
41  * Module descriptor
42  *****************************************************************************/
43 static int   InOpen ( vlc_object_t * );
44 static void  InClose( vlc_object_t * );
45 static int  OutOpen ( vlc_object_t * );
46 static void OutClose( vlc_object_t * );
47
48 #define CACHING_TEXT N_("Caching value in ms")
49 #define CACHING_LONGTEXT N_( \
50     "Caching value for FTP streams. This " \
51     "value should be set in milliseconds." )
52 #define USER_TEXT N_("FTP user name")
53 #define USER_LONGTEXT N_("User name that will " \
54     "be used for the connection.")
55 #define PASS_TEXT N_("FTP password")
56 #define PASS_LONGTEXT N_("Password that will be " \
57     "used for the connection.")
58 #define ACCOUNT_TEXT N_("FTP account")
59 #define ACCOUNT_LONGTEXT N_("Account that will be " \
60     "used for the connection.")
61
62 vlc_module_begin();
63     set_shortname( "FTP" );
64     set_description( _("FTP input") );
65     set_capability( "access2", 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, VLC_TRUE );
70     add_string( "ftp-user", "anonymous", NULL, USER_TEXT, USER_LONGTEXT,
71                 VLC_FALSE );
72     add_string( "ftp-pwd", "anonymous@example.com", NULL, PASS_TEXT,
73                 PASS_LONGTEXT, VLC_FALSE );
74     add_string( "ftp-account", "anonymous", NULL, ACCOUNT_TEXT,
75                 ACCOUNT_LONGTEXT, VLC_FALSE );
76     add_shortcut( "ftp" );
77     set_callbacks( InOpen, InClose );
78
79     add_submodule();
80     set_shortname( "FTP" );
81     set_description( _("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 /*****************************************************************************
90  * Local prototypes
91  *****************************************************************************/
92 static int Read( access_t *, uint8_t *, int );
93 static int Write( sout_access_out_t *, block_t * );
94 static int Seek( access_t *, int64_t );
95 static int OutSeek( sout_access_out_t *, int64_t );
96 static int Control( access_t *, int, va_list );
97
98 struct access_sys_t
99 {
100     vlc_url_t  url;
101
102     int        fd_cmd;
103     int        fd_data;
104
105     char       sz_epsv_ip[NI_MAXNUMERICHOST];
106 };
107 #define GET_OUT_SYS( p_this ) \
108     ((access_sys_t *)(((sout_access_out_t *)(p_this))->p_sys))
109
110 static int ftp_SendCommand( vlc_object_t *, access_sys_t *, const char *, ... );
111 static int ftp_ReadCommand( vlc_object_t *, access_sys_t *, int *, char ** );
112 static int ftp_StartStream( vlc_object_t *, access_sys_t *, int64_t );
113 static int ftp_StopStream ( vlc_object_t *, access_sys_t * );
114
115 static int Login( vlc_object_t *p_access, access_sys_t *p_sys )
116 {
117     int i_answer;
118     char *psz;
119
120     /* *** Open a TCP connection with server *** */
121     int fd = p_sys->fd_cmd = net_ConnectTCP( p_access, p_sys->url.psz_host,
122                                              p_sys->url.i_port );
123     if( fd == -1 )
124     {
125         msg_Err( p_access, "connection failed" );
126         intf_UserFatal( p_access, VLC_FALSE, _("Network interaction failed"), 
127                         _("VLC could not connect with the given server.") );
128         return -1;
129     }
130
131     while( ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) == 1 );
132
133     if( i_answer / 100 != 2 )
134     {
135         msg_Err( p_access, "connection rejected" );
136         intf_UserFatal( p_access, VLC_FALSE, _("Network interaction failed"), 
137                         _("VLC's connection to the given server was rejected.") );
138         return -1;
139     }
140
141     msg_Dbg( p_access, "connection accepted (%d)", i_answer );
142
143     if( p_sys->url.psz_username && *p_sys->url.psz_username )
144         psz = strdup( p_sys->url.psz_username );
145     else
146         psz = var_CreateGetString( p_access, "ftp-user" );
147
148     if( ftp_SendCommand( p_access, p_sys, "USER %s", psz ) < 0 ||
149         ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 )
150     {
151         free( psz );
152         return -1;
153     }
154     free( psz );
155
156     switch( i_answer / 100 )
157     {
158         case 2:
159             msg_Dbg( p_access, "user accepted" );
160             break;
161         case 3:
162             msg_Dbg( p_access, "password needed" );
163             if( p_sys->url.psz_password && *p_sys->url.psz_password )
164                 psz = strdup( p_sys->url.psz_password );
165             else
166                 psz = var_CreateGetString( p_access, "ftp-pwd" );
167
168             if( ftp_SendCommand( p_access, p_sys, "PASS %s", psz ) < 0 ||
169                 ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 )
170             {
171                 free( psz );
172                 return -1;
173             }
174             free( psz );
175
176             switch( i_answer / 100 )
177             {
178                 case 2:
179                     msg_Dbg( p_access, "password accepted" );
180                     break;
181                 case 3:
182                     msg_Dbg( p_access, "account needed" );
183                     psz = var_CreateGetString( p_access, "ftp-account" );
184                     if( ftp_SendCommand( p_access, p_sys, "ACCT %s",
185                                          psz ) < 0 ||
186                         ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) < 0 )
187                     {
188                         free( psz );
189                         return -1;
190                     }
191                     free( psz );
192
193                     if( i_answer / 100 != 2 )
194                     {
195                         msg_Err( p_access, "account rejected" );
196                         intf_UserFatal( p_access, VLC_FALSE, 
197                                         _("Network interaction failed"), 
198                                         _("Your account was rejected.") );
199                         return -1;
200                     }
201                     msg_Dbg( p_access, "account accepted" );
202                     break;
203
204                 default:
205                     msg_Err( p_access, "password rejected" );
206                     intf_UserFatal( p_access, VLC_FALSE, 
207                                     _("Network interaction failed"), 
208                                     _("Your password was rejected.") );
209                     return -1;
210             }
211             break;
212         default:
213             msg_Err( p_access, "user rejected" );
214             intf_UserFatal( p_access, VLC_FALSE, 
215                         _("Network interaction failed"), 
216                         _("Your connection attemp to the server was rejected.") );
217             return -1;
218     }
219
220     return 0;
221 }
222
223 static int Connect( vlc_object_t *p_access, access_sys_t *p_sys )
224 {
225     if( Login( p_access, p_sys ) < 0 )
226         return -1;
227
228     /* Extended passive mode */
229     if( ftp_SendCommand( p_access, p_sys, "EPSV ALL" ) < 0 )
230     {
231         msg_Err( p_access, "cannot request extended passive mode" );
232         net_Close( p_sys->fd_cmd );
233         return -1;
234     }
235
236     if( ftp_ReadCommand( p_access, p_sys, NULL, NULL ) == 2 )
237     {
238         if( net_GetPeerAddress( p_sys->fd_cmd, p_sys->sz_epsv_ip, NULL ) )
239         {
240             net_Close( p_sys->fd_cmd );
241             return -1;
242         }
243     }
244     else
245     {
246         /* If ESPV ALL fails, we fallback to PASV.
247          * We have to restart the connection in case there is a NAT that
248          * understands EPSV ALL in the way, and hence won't allow PASV on
249          * the initial connection.
250          */
251         msg_Info( p_access, "FTP Extended passive mode disabled" );
252         net_Close( p_sys->fd_cmd );
253
254         if( Login( p_access, p_sys ) )
255         {
256             net_Close( p_sys->fd_cmd );
257             return -1;
258         }
259     }
260
261     /* check binary mode support */
262     if( ftp_SendCommand( p_access, p_sys, "TYPE I" ) < 0 ||
263         ftp_ReadCommand( p_access, p_sys, NULL, NULL ) != 2 )
264     {
265         msg_Err( p_access, "cannot set binary transfer mode" );
266         net_Close( p_sys->fd_cmd );
267         return -1;
268     }
269
270     return 0;
271 }
272
273
274 static int parseURL( vlc_url_t *url, const char *path )
275 {
276     if( path == NULL )
277         return -1;
278
279     /* *** Parse URL and get server addr/port and path *** */
280     while( *path == '/' )
281         path++;
282
283     vlc_UrlParse( url, path, 0 );
284
285     if( url->psz_host == NULL || *url->psz_host == '\0' )
286         return -1;
287
288     if( url->i_port <= 0 )
289         url->i_port = IPPORT_FTP; /* default port */
290
291     /* FTP URLs are relative to user's default directory (RFC1738)
292     For absolute path use ftp://foo.bar//usr/local/etc/filename */
293
294     if( *url->psz_path == '/' )
295         url->psz_path++;
296
297     return 0;
298 }
299
300
301 /****************************************************************************
302  * Open: connect to ftp server and ask for file
303  ****************************************************************************/
304 static int InOpen( vlc_object_t *p_this )
305 {
306     access_t     *p_access = (access_t*)p_this;
307     access_sys_t *p_sys;
308     char         *psz_arg;
309
310     /* Init p_access */
311     STANDARD_READ_ACCESS_INIT
312     p_sys->fd_data = -1;
313
314     if( parseURL( &p_sys->url, p_access->psz_path ) )
315         goto exit_error;
316
317     if( Connect( p_this, p_sys ) )
318         goto exit_error;
319
320     /* get size */
321     if( ftp_SendCommand( p_this, p_sys, "SIZE %s", p_sys->url.psz_path ) < 0 ||
322         ftp_ReadCommand( p_this, p_sys, NULL, &psz_arg ) != 2 )
323     {
324         msg_Err( p_access, "cannot get file size" );
325         net_Close( p_sys->fd_cmd );
326         goto exit_error;
327     }
328     p_access->info.i_size = atoll( &psz_arg[4] );
329     free( psz_arg );
330     msg_Dbg( p_access, "file size: "I64Fd, p_access->info.i_size );
331
332     /* Start the 'stream' */
333     if( ftp_StartStream( p_this, p_sys, 0 ) < 0 )
334     {
335         msg_Err( p_access, "cannot retrieve file" );
336         net_Close( p_sys->fd_cmd );
337         goto exit_error;
338     }
339
340     /* Update default_pts to a suitable value for ftp access */
341     var_Create( p_access, "ftp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
342
343     return VLC_SUCCESS;
344
345 exit_error:
346     vlc_UrlClean( &p_sys->url );
347     free( p_sys );
348     return VLC_EGENERIC;
349 }
350
351 static int OutOpen( vlc_object_t *p_this )
352 {
353     sout_access_out_t *p_access = (sout_access_out_t *)p_this;
354     access_sys_t      *p_sys;
355
356     p_sys = malloc( sizeof( *p_sys ) );
357     if( p_sys == NULL )
358         return VLC_ENOMEM;
359     memset( p_sys, 0, sizeof( *p_sys ) );
360
361     /* Init p_access */
362     p_sys->fd_data = -1;
363
364     if( parseURL( &p_sys->url, p_access->psz_name ) )
365         goto exit_error;
366
367     if( Connect( p_this, p_sys ) )
368         goto exit_error;
369
370     /* Start the 'stream' */
371     if( ftp_StartStream( p_this, p_sys, 0 ) < 0 )
372     {
373         msg_Err( p_access, "cannot store file" );
374         net_Close( p_sys->fd_cmd );
375         goto exit_error;
376     }
377
378     p_access->pf_seek = OutSeek;
379     p_access->pf_write = Write;
380
381     return VLC_SUCCESS;
382
383 exit_error:
384     vlc_UrlClean( &p_sys->url );
385     free( p_sys );
386     return VLC_EGENERIC;
387 }
388
389 /*****************************************************************************
390  * Close: free unused data structures
391  *****************************************************************************/
392 static void Close( vlc_object_t *p_access, access_sys_t *p_sys )
393 {
394     msg_Dbg( p_access, "stopping stream" );
395     ftp_StopStream( p_access, p_sys );
396
397     if( ftp_SendCommand( p_access, p_sys, "QUIT" ) < 0 )
398     {
399         msg_Warn( p_access, "cannot quit" );
400     }
401     else
402     {
403         ftp_ReadCommand( p_access, p_sys, NULL, NULL );
404     }
405     net_Close( p_sys->fd_cmd );
406
407     /* free memory */
408     vlc_UrlClean( &p_sys->url );
409     free( p_sys );
410 }
411
412 static void InClose( vlc_object_t *p_this )
413 {
414     Close( p_this, ((access_t *)p_this)->p_sys);
415 }
416
417 static void OutClose( vlc_object_t *p_this )
418 {
419     Close( p_this, GET_OUT_SYS(p_this));
420 }
421
422
423 /*****************************************************************************
424  * Seek: try to go at the right place
425  *****************************************************************************/
426 static int _Seek( vlc_object_t *p_access, access_sys_t *p_sys, int64_t i_pos )
427 {
428     if( i_pos < 0 )
429         return VLC_EGENERIC;
430
431     msg_Dbg( p_access, "seeking to "I64Fd, i_pos );
432
433     ftp_StopStream( (vlc_object_t *)p_access, p_sys );
434     if( ftp_StartStream( (vlc_object_t *)p_access, p_sys, i_pos ) < 0 )
435         return VLC_EGENERIC;
436
437     return VLC_SUCCESS;
438 }
439
440 static int Seek( access_t *p_access, int64_t i_pos )
441 {
442     int val = _Seek( (vlc_object_t *)p_access, p_access->p_sys, i_pos );
443     if( val )
444         return val;
445
446     p_access->info.b_eof = VLC_FALSE;
447     p_access->info.i_pos = i_pos;
448
449     return VLC_SUCCESS;
450 }
451
452 static int OutSeek( sout_access_out_t *p_access, off_t i_pos )
453 {
454     return _Seek( (vlc_object_t *)p_access, GET_OUT_SYS( p_access ), i_pos);
455 }
456
457 /*****************************************************************************
458  * Read:
459  *****************************************************************************/
460 static int Read( access_t *p_access, uint8_t *p_buffer, int i_len )
461 {
462     access_sys_t *p_sys = p_access->p_sys;
463     int i_read;
464
465     assert( p_sys->fd_data != -1 );
466     assert( p_access->i_object_type == VLC_OBJECT_ACCESS );
467
468     if( p_access->info.b_eof )
469         return 0;
470
471     i_read = net_Read( p_access, p_sys->fd_data, NULL, p_buffer, i_len,
472                        VLC_FALSE );
473     if( i_read == 0 )
474         p_access->info.b_eof = VLC_TRUE;
475     else if( i_read > 0 )
476         p_access->info.i_pos += i_read;
477
478     return i_read;
479 }
480
481 /*****************************************************************************
482  * Write:
483  *****************************************************************************/
484 static int Write( sout_access_out_t *p_access, block_t *p_buffer )
485 {
486     access_sys_t *p_sys = GET_OUT_SYS(p_access);
487     size_t i_write = 0;
488
489     assert( p_sys->fd_data != -1 );
490
491     while( p_buffer != NULL )
492     {
493         block_t *p_next = p_buffer->p_next;;
494
495         i_write += net_Write( p_access, p_sys->fd_data, NULL,
496                               p_buffer->p_buffer, p_buffer->i_buffer );
497         block_Release( p_buffer );
498
499         p_buffer = p_next;
500     }
501
502     return i_write;
503 }
504
505 /*****************************************************************************
506  * Control:
507  *****************************************************************************/
508 static int Control( access_t *p_access, int i_query, va_list args )
509 {
510     vlc_bool_t   *pb_bool;
511     int          *pi_int;
512     int64_t      *pi_64;
513     vlc_value_t  val;
514
515     switch( i_query )
516     {
517         /* */
518         case ACCESS_CAN_SEEK:
519             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
520             *pb_bool = VLC_TRUE;
521             break;
522         case ACCESS_CAN_FASTSEEK:
523             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
524             *pb_bool = VLC_FALSE;
525             break;
526         case ACCESS_CAN_PAUSE:
527             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
528             *pb_bool = VLC_TRUE;    /* FIXME */
529             break;
530         case ACCESS_CAN_CONTROL_PACE:
531             pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* );
532             *pb_bool = VLC_TRUE;    /* FIXME */
533             break;
534
535         /* */
536         case ACCESS_GET_MTU:
537             pi_int = (int*)va_arg( args, int * );
538             *pi_int = 0;
539             break;
540
541         case ACCESS_GET_PTS_DELAY:
542             pi_64 = (int64_t*)va_arg( args, int64_t * );
543             var_Get( p_access, "ftp-caching", &val );
544             *pi_64 = (int64_t)var_GetInteger( p_access, "ftp-caching" ) * I64C(1000);
545             break;
546
547         /* */
548         case ACCESS_SET_PAUSE_STATE:
549             /* Nothing to do */
550             break;
551
552         case ACCESS_GET_TITLE_INFO:
553         case ACCESS_SET_TITLE:
554         case ACCESS_SET_SEEKPOINT:
555         case ACCESS_SET_PRIVATE_ID_STATE:
556             return VLC_EGENERIC;
557
558         default:
559             msg_Warn( p_access, "unimplemented query in control" );
560             return VLC_EGENERIC;
561
562     }
563     return VLC_SUCCESS;
564 }
565
566 /*****************************************************************************
567  * ftp_*:
568  *****************************************************************************/
569 static int ftp_SendCommand( vlc_object_t *p_access, access_sys_t *p_sys,
570                             const char *psz_fmt, ... )
571 {
572     va_list      args;
573     char         *psz_cmd;
574
575     va_start( args, psz_fmt );
576     vasprintf( &psz_cmd, psz_fmt, args );
577     va_end( args );
578
579     msg_Dbg( p_access, "ftp_SendCommand:\"%s\"", psz_cmd);
580     if( net_Printf( VLC_OBJECT(p_access), p_sys->fd_cmd, NULL, "%s\r\n",
581                     psz_cmd ) < 0 )
582     {
583         msg_Err( p_access, "failed to send command" );
584         return VLC_EGENERIC;
585     }
586     return VLC_SUCCESS;
587 }
588
589 /* TODO support this s**t :
590  RFC 959 allows the client to send certain TELNET strings at any moment,
591  even in the middle of a request:
592
593  * \377\377.
594  * \377\376x where x is one byte.
595  * \377\375x where x is one byte. The server is obliged to send \377\374x
596  *                                immediately after reading x.
597  * \377\374x where x is one byte.
598  * \377\373x where x is one byte. The server is obliged to send \377\376x
599  *                                immediately after reading x.
600  * \377x for any other byte x.
601
602  These strings are not part of the requests, except in the case \377\377,
603  where the request contains one \377. */
604 static int ftp_ReadCommand( vlc_object_t *p_access, access_sys_t *p_sys,
605                             int *pi_answer, char **ppsz_answer )
606 {
607     char         *psz_line;
608     int          i_answer;
609
610     psz_line = net_Gets( p_access, p_sys->fd_cmd, NULL );
611     msg_Dbg( p_access, "answer=%s", psz_line );
612     if( psz_line == NULL || strlen( psz_line ) < 3 )
613     {
614         msg_Err( p_access, "cannot get answer" );
615         if( psz_line ) free( psz_line );
616         if( pi_answer ) *pi_answer    = 500;
617         if( ppsz_answer ) *ppsz_answer  = NULL;
618         return -1;
619     }
620
621     if( psz_line[3] == '-' )    /* Multiple response */
622     {
623         char end[4];
624
625         memcpy( end, psz_line, 3 );
626         end[3] = ' ';
627
628         for( ;; )
629         {
630             char *psz_tmp = net_Gets( p_access, p_sys->fd_cmd, NULL );
631
632             if( psz_tmp == NULL )   /* Error */
633                 break;
634
635             if( !strncmp( psz_tmp, end, 4 ) )
636             {
637                 free( psz_tmp );
638                 break;
639             }
640             free( psz_tmp );
641         }
642     }
643
644     i_answer = atoi( psz_line );
645
646     if( pi_answer ) *pi_answer = i_answer;
647     if( ppsz_answer )
648     {
649         *ppsz_answer = psz_line;
650     }
651     else
652     {
653         free( psz_line );
654     }
655     return( i_answer / 100 );
656 }
657
658 static int ftp_StartStream( vlc_object_t *p_access, access_sys_t *p_sys,
659                             off_t i_start )
660 {
661     char psz_ipv4[16], *psz_ip = p_sys->sz_epsv_ip;
662     int  i_answer;
663     char *psz_arg, *psz_parser;
664     int  i_port;
665
666     assert( p_sys->fd_data == -1 );
667
668     if( ( ftp_SendCommand( p_access, p_sys, *psz_ip ? "EPSV" : "PASV" ) < 0 )
669      || ( ftp_ReadCommand( p_access, p_sys, &i_answer, &psz_arg ) != 2 ) )
670     {
671         msg_Err( p_access, "cannot set passive mode" );
672         return VLC_EGENERIC;
673     }
674
675     psz_parser = strchr( psz_arg, '(' );
676     if( psz_parser == NULL )
677     {
678         free( psz_arg );
679         msg_Err( p_access, "cannot parse passive mode response" );
680         return VLC_EGENERIC;
681     }
682
683     if( psz_ip != NULL )
684     {
685         char psz_fmt[7] = "(|||%u";
686         psz_fmt[1] = psz_fmt[2] = psz_fmt[3] = psz_parser[1];
687
688         if( sscanf( psz_parser, psz_fmt, &i_port ) < 1 )
689         {
690             free( psz_arg );
691             msg_Err( p_access, "cannot parse passive mode response" );
692             return VLC_EGENERIC;
693         }
694     }
695     else
696     {
697         unsigned  a1, a2, a3, a4, p1, p2;
698
699         if( ( sscanf( psz_parser, "(%u,%u,%u,%u,%u,%u", &a1, &a2, &a3, &a4,
700                       &p1, &p2 ) < 6 ) || ( a1 > 255 ) || ( a2 > 255 )
701          || ( a3 > 255 ) || ( a4 > 255 ) || ( p1 > 255 ) || ( p2 > 255 ) )
702         {
703             free( psz_arg );
704             msg_Err( p_access, "cannot parse passive mode response" );
705             return VLC_EGENERIC;
706         }
707
708         sprintf( psz_ipv4, "%u.%u.%u.%u", a1, a2, a3, a4 );
709         psz_ip = psz_ipv4;
710         i_port = (p1 << 8) | p2;
711     }
712     free( psz_arg );
713
714     msg_Dbg( p_access, "ip:%s port:%d", psz_ip, i_port );
715
716     if( ftp_SendCommand( p_access, p_sys, "TYPE I" ) < 0 ||
717         ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) != 2 )
718     {
719         msg_Err( p_access, "cannot set binary transfer mode" );
720         return VLC_EGENERIC;
721     }
722
723     if( i_start > 0 )
724     {
725         if( ftp_SendCommand( p_access, p_sys, "REST "I64Fu, i_start ) < 0 ||
726             ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) > 3 )
727         {
728             msg_Err( p_access, "cannot set restart offset" );
729             return VLC_EGENERIC;
730         }
731     }
732
733     msg_Dbg( p_access, "waiting for data connection..." );
734     p_sys->fd_data = net_ConnectTCP( p_access, psz_ip, i_port );
735     if( p_sys->fd_data < 0 )
736     {
737         msg_Err( p_access, "failed to connect with server" );
738         return VLC_EGENERIC;
739     }
740     msg_Dbg( p_access, "connection with \"%s:%d\" successful",
741              psz_ip, i_port );
742
743     /* "1xx" message */
744     if( ftp_SendCommand( p_access, p_sys, "%s %s",
745                          (p_access->i_object_type == VLC_OBJECT_ACCESS)
746                                  ? "RETR" : "STOR",
747                          p_sys->url.psz_path ) < 0 ||
748         ftp_ReadCommand( p_access, p_sys, &i_answer, NULL ) > 2 )
749     {
750         msg_Err( p_access, "cannot retreive file" );
751         return VLC_EGENERIC;
752     }
753     return VLC_SUCCESS;
754 }
755
756 static int ftp_StopStream ( vlc_object_t *p_access, access_sys_t *p_sys )
757 {
758     if( ftp_SendCommand( p_access, p_sys, "ABOR" ) < 0 )
759     {
760         msg_Warn( p_access, "cannot abort file" );
761         if(  p_sys->fd_data > 0 )
762             net_Close( p_sys->fd_data );
763         p_sys->fd_data = -1;
764         return VLC_EGENERIC;
765     }
766
767     if( p_sys->fd_data != -1 )
768     {
769         net_Close( p_sys->fd_data );
770         p_sys->fd_data = -1;
771         ftp_ReadCommand( p_access, p_sys, NULL, NULL );
772     }
773     ftp_ReadCommand( p_access, p_sys, NULL, NULL );
774
775     return VLC_SUCCESS;
776 }