1 /*****************************************************************************
2 * ftp.c: FTP input module
3 *****************************************************************************
4 * Copyright (C) 2001-2006 VLC authors and VideoLAN
5 * Copyright © 2006 Rémi Denis-Courmont
8 * Authors: Laurent Aimar <fenrir@via.ecp.fr> - original code
9 * Rémi Denis-Courmont <rem # videolan.org> - EPSV support
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
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 Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
38 #include <vlc_access.h>
39 #include <vlc_dialog.h>
41 #include <vlc_network.h>
45 #include <vlc_charset.h>
48 # define IPPORT_FTP 21u
52 # define IPPORT_FTPS 990u
55 /*****************************************************************************
57 *****************************************************************************/
58 static int InOpen ( vlc_object_t
* );
59 static void InClose( vlc_object_t
* );
61 static int OutOpen ( vlc_object_t
* );
62 static void OutClose( vlc_object_t
* );
65 #define USER_TEXT N_("FTP user name")
66 #define USER_LONGTEXT N_("User name that will " \
67 "be used for the connection.")
68 #define PASS_TEXT N_("FTP password")
69 #define PASS_LONGTEXT N_("Password that will be " \
70 "used for the connection.")
71 #define ACCOUNT_TEXT N_("FTP account")
72 #define ACCOUNT_LONGTEXT N_("Account that will be " \
73 "used for the connection.")
76 set_shortname( "FTP" )
77 set_description( N_("FTP input") )
78 set_capability( "access", 0 )
79 set_category( CAT_INPUT
)
80 set_subcategory( SUBCAT_INPUT_ACCESS
)
81 add_string( "ftp-user", "anonymous", USER_TEXT
, USER_LONGTEXT
,
83 add_string( "ftp-pwd", "anonymous@example.com", PASS_TEXT
,
84 PASS_LONGTEXT
, false )
85 add_string( "ftp-account", "anonymous", ACCOUNT_TEXT
,
86 ACCOUNT_LONGTEXT
, false )
87 add_shortcut( "ftp", "ftps", "ftpes" )
88 set_callbacks( InOpen
, InClose
)
92 set_shortname( "FTP" )
93 set_description( N_("FTP upload output") )
94 set_capability( "sout access", 0 )
95 set_category( CAT_SOUT
)
96 set_subcategory( SUBCAT_SOUT_ACO
)
97 add_shortcut( "ftp", "ftps", "ftpes" )
98 set_callbacks( OutOpen
, OutClose
)
102 /*****************************************************************************
104 *****************************************************************************/
105 static ssize_t
Read( access_t
*, uint8_t *, size_t );
106 static int Seek( access_t
*, uint64_t );
107 static int Control( access_t
*, int, va_list );
109 static int OutSeek( sout_access_out_t
*, off_t
);
110 static ssize_t
Write( sout_access_out_t
*, block_t
* );
113 static void FeaturesCheck( void *, const char * );
115 typedef struct ftp_features_t
132 ftp_features_t features
;
133 vlc_tls_creds_t
*p_creds
;
134 enum tls_mode_e tlsmode
;
142 char sz_epsv_ip
[NI_MAXNUMERICHOST
];
147 #define GET_OUT_SYS( p_this ) \
148 ((access_sys_t *)(((sout_access_out_t *)(p_this))->p_sys))
150 static int ftp_SendCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
151 const char *fmt
, ... )
153 size_t fmtlen
= strlen( fmt
);
154 char fmtbuf
[fmtlen
+ 3];
156 memcpy( fmtbuf
, fmt
, fmtlen
);
157 memcpy( fmtbuf
+ fmtlen
, "\r\n", 3 );
163 va_start( args
, fmt
);
164 val
= vasprintf( &cmd
, fmtbuf
, args
);
166 if( unlikely(val
== -1) )
169 msg_Dbg( obj
, "sending request: \"%.*s\" (%d bytes)", val
- 2, cmd
, val
);
170 if( net_Write( obj
, sys
->cmd
.fd
, sys
->cmd
.p_vs
, cmd
, val
) != val
)
172 msg_Err( obj
, "request failure" );
181 /* TODO support this s**t :
182 RFC 959 allows the client to send certain TELNET strings at any moment,
183 even in the middle of a request:
186 * \377\376x where x is one byte.
187 * \377\375x where x is one byte. The server is obliged to send \377\374x
188 * immediately after reading x.
189 * \377\374x where x is one byte.
190 * \377\373x where x is one byte. The server is obliged to send \377\376x
191 * immediately after reading x.
192 * \377x for any other byte x.
194 These strings are not part of the requests, except in the case \377\377,
195 where the request contains one \377. */
196 static int ftp_RecvAnswer( vlc_object_t
*obj
, access_sys_t
*sys
,
197 int *restrict codep
, char **restrict strp
,
198 void (*cb
)(void *, const char *), void *opaque
)
205 char *resp
= net_Gets( obj
, sys
->cmd
.fd
, sys
->cmd
.p_vs
);
208 msg_Err( obj
, "response failure" );
213 unsigned code
= strtoul( resp
, &end
, 10 );
214 if( (end
- resp
) != 3 || (*end
!= '-' && *end
!= ' ') )
216 msg_Err( obj
, "malformatted response" );
219 msg_Dbg( obj
, "received response: \"%s\"", resp
);
221 if( *end
== '-' ) /* Multi-line response */
228 char *line
= net_Gets( obj
, sys
->cmd
.fd
, sys
->cmd
.p_vs
);
231 msg_Err( obj
, "response failure" );
235 done
= !strncmp( resp
, line
, 4 );
255 static void DummyLine( void *data
, const char *str
)
257 (void) data
; (void) str
;
260 static int ftp_RecvCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
261 int *restrict codep
, char **restrict strp
)
263 return ftp_RecvAnswer( obj
, sys
, codep
, strp
, DummyLine
, NULL
);
266 static int ftp_StartStream( vlc_object_t
*, access_sys_t
*, uint64_t );
267 static int ftp_StopStream ( vlc_object_t
*, access_sys_t
* );
269 static void readTLSMode( access_sys_t
*p_sys
, const char * psz_access
)
271 if ( !strncmp( psz_access
, "ftps", 4 ) )
272 p_sys
->tlsmode
= IMPLICIT
;
274 if ( !strncmp( psz_access
, "ftpes", 5 ) )
275 p_sys
->tlsmode
= EXPLICIT
;
277 p_sys
->tlsmode
= NONE
;
280 static int createCmdTLS( vlc_object_t
*p_access
, access_sys_t
*p_sys
, int fd
,
281 const char *psz_session_name
)
283 p_sys
->p_creds
= vlc_tls_ClientCreate( p_access
);
284 if( p_sys
->p_creds
== NULL
) return -1;
286 /* TLS/SSL handshake */
287 p_sys
->cmd
.p_tls
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
, fd
,
291 if( p_sys
->cmd
.p_tls
== NULL
)
293 msg_Err( p_access
, "cannot establish FTP/TLS session on command channel" );
296 p_sys
->cmd
.p_vs
= &p_sys
->cmd
.p_tls
->sock
;
301 static void clearCmdTLS( access_sys_t
*p_sys
)
303 if ( p_sys
->cmd
.p_tls
) vlc_tls_SessionDelete( p_sys
->cmd
.p_tls
);
304 if ( p_sys
->p_creds
) vlc_tls_Delete( p_sys
->p_creds
);
305 p_sys
->cmd
.p_tls
= NULL
;
306 p_sys
->cmd
.p_vs
= NULL
;
307 p_sys
->p_creds
= NULL
;
310 static int Login( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
315 /* *** Open a TCP connection with server *** */
316 int fd
= p_sys
->cmd
.fd
= net_ConnectTCP( p_access
, p_sys
->url
.psz_host
,
320 msg_Err( p_access
, "connection failed" );
321 dialog_Fatal( p_access
, _("Network interaction failed"), "%s",
322 _("VLC could not connect with the given server.") );
326 if ( p_sys
->tlsmode
== IMPLICIT
) /* FTPS Mode */
328 if ( createCmdTLS( p_access
, p_sys
, fd
, "ftps") < 0 )
332 while( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) == 1 );
334 if( i_answer
/ 100 != 2 )
336 msg_Err( p_access
, "connection rejected" );
337 dialog_Fatal( p_access
, _("Network interaction failed"), "%s",
338 _("VLC's connection to the given server was rejected.") );
342 msg_Dbg( p_access
, "connection accepted (%d)", i_answer
);
344 if( p_sys
->url
.psz_username
&& *p_sys
->url
.psz_username
)
345 psz
= strdup( p_sys
->url
.psz_username
);
347 psz
= var_InheritString( p_access
, "ftp-user" );
351 /* Features check first */
352 if( ftp_SendCommand( p_access
, p_sys
, "FEAT" ) < 0
353 || ftp_RecvAnswer( p_access
, p_sys
, NULL
, NULL
,
354 FeaturesCheck
, &p_sys
->features
) < 0 )
356 msg_Err( p_access
, "cannot get server features" );
360 /* Create TLS Session */
361 if( p_sys
->tlsmode
== EXPLICIT
)
363 if ( ! p_sys
->features
.b_authtls
)
365 msg_Err( p_access
, "Server does not support TLS" );
369 if( ftp_SendCommand( p_access
, p_sys
, "AUTH TLS" ) < 0
370 || ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0
373 msg_Err( p_access
, "cannot switch to TLS: server replied with code %d",
378 if ( createCmdTLS( p_access
, p_sys
, fd
, "ftpes") < 0 )
384 if( p_sys
->tlsmode
!= NONE
)
386 if( ftp_SendCommand( p_access
, p_sys
, "PBSZ 0" ) < 0 ||
387 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
390 msg_Err( p_access
, "Can't truncate Protection buffer size for TLS" );
395 if( ftp_SendCommand( p_access
, p_sys
, "PROT P" ) < 0 ||
396 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
399 msg_Err( p_access
, "Can't set Data channel protection" );
405 /* Send credentials over channel */
406 if( ftp_SendCommand( p_access
, p_sys
, "USER %s", psz
) < 0 ||
407 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
414 switch( i_answer
/ 100 )
417 /* X.509 auth successful after AUTH TLS / RFC 2228 sec. 4 */
418 if ( i_answer
== 232 )
419 msg_Dbg( p_access
, "user accepted and authenticated" );
421 msg_Dbg( p_access
, "user accepted" );
424 msg_Dbg( p_access
, "password needed" );
425 if( p_sys
->url
.psz_password
&& *p_sys
->url
.psz_password
)
426 psz
= strdup( p_sys
->url
.psz_password
);
428 psz
= var_InheritString( p_access
, "ftp-pwd" );
432 if( ftp_SendCommand( p_access
, p_sys
, "PASS %s", psz
) < 0 ||
433 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
440 switch( i_answer
/ 100 )
443 msg_Dbg( p_access
, "password accepted" );
446 msg_Dbg( p_access
, "account needed" );
447 psz
= var_InheritString( p_access
, "ftp-account" );
448 if( ftp_SendCommand( p_access
, p_sys
, "ACCT %s",
450 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
457 if( i_answer
/ 100 != 2 )
459 msg_Err( p_access
, "account rejected" );
460 dialog_Fatal( p_access
,
461 _("Network interaction failed"),
462 "%s", _("Your account was rejected.") );
465 msg_Dbg( p_access
, "account accepted" );
469 msg_Err( p_access
, "password rejected" );
470 dialog_Fatal( p_access
, _("Network interaction failed"),
471 "%s", _("Your password was rejected.") );
476 msg_Err( p_access
, "user rejected" );
477 dialog_Fatal( p_access
, _("Network interaction failed"), "%s",
478 _("Your connection attempt to the server was rejected.") );
485 clearCmdTLS( p_sys
);
489 static void FeaturesCheck( void *opaque
, const char *feature
)
491 ftp_features_t
*features
= opaque
;
493 if( strcasestr( feature
, "UTF8" ) != NULL
)
494 features
->b_unicode
= true;
496 if( strcasestr( feature
, "AUTH TLS" ) != NULL
)
497 features
->b_authtls
= true;
500 static const char *IsASCII( const char *str
)
503 for( const char *p
= str
; (c
= *p
) != '\0'; p
++ )
509 static int Connect( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
511 if( Login( p_access
, p_sys
) < 0 )
514 /* Extended passive mode */
515 if( ftp_SendCommand( p_access
, p_sys
, "EPSV ALL" ) < 0 )
517 msg_Err( p_access
, "cannot request extended passive mode" );
521 if( ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) == 2 )
523 if( net_GetPeerAddress( p_sys
->cmd
.fd
, p_sys
->sz_epsv_ip
, NULL
) )
528 /* If ESPV ALL fails, we fallback to PASV.
529 * We have to restart the connection in case there is a NAT that
530 * understands EPSV ALL in the way, and hence won't allow PASV on
531 * the initial connection.
533 msg_Info( p_access
, "FTP Extended passive mode disabled" );
534 clearCmdTLS( p_sys
);
535 net_Close( p_sys
->cmd
.fd
);
537 if( Login( p_access
, p_sys
) )
541 if( (p_sys
->features
.b_unicode
? IsUTF8
: IsASCII
)(p_sys
->url
.psz_path
) == NULL
)
543 msg_Err( p_access
, "unsupported path: \"%s\"", p_sys
->url
.psz_path
);
547 /* check binary mode support */
548 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
549 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) != 2 )
551 msg_Err( p_access
, "cannot set binary transfer mode" );
558 clearCmdTLS( p_sys
);
559 net_Close( p_sys
->cmd
.fd
);
564 static int parseURL( vlc_url_t
*url
, const char *path
, enum tls_mode_e mode
)
569 /* *** Parse URL and get server addr/port and path *** */
570 while( *path
== '/' )
573 vlc_UrlParse( url
, path
, 0 );
575 if( url
->psz_host
== NULL
|| *url
->psz_host
== '\0' )
578 if( url
->i_port
<= 0 )
580 if( mode
== IMPLICIT
)
581 url
->i_port
= IPPORT_FTPS
;
583 url
->i_port
= IPPORT_FTP
; /* default port */
586 if( url
->psz_path
== NULL
)
588 /* FTP URLs are relative to user's default directory (RFC1738 §3.2)
589 For absolute path use ftp://foo.bar//usr/local/etc/filename */
590 /* FIXME: we should issue a series of CWD, one per slash */
593 assert( url
->psz_path
[0] == '/' );
597 char *type
= strstr( url
->psz_path
, ";type=" );
601 if( strchr( "iI", type
[6] ) == NULL
)
602 return VLC_EGENERIC
; /* ASCII and directory not supported */
604 decode_URI( url
->psz_path
);
609 /****************************************************************************
610 * Open: connect to ftp server and ask for file
611 ****************************************************************************/
612 static int InOpen( vlc_object_t
*p_this
)
614 access_t
*p_access
= (access_t
*)p_this
;
619 STANDARD_READ_ACCESS_INIT
622 p_sys
->directory
= false;
624 readTLSMode( p_sys
, p_access
->psz_access
);
626 if( parseURL( &p_sys
->url
, p_access
->psz_location
, p_sys
->tlsmode
) )
629 if( Connect( p_this
, p_sys
) )
633 if( p_sys
->url
.psz_path
== NULL
)
634 p_sys
->directory
= true;
636 if( ftp_SendCommand( p_this
, p_sys
, "SIZE %s", p_sys
->url
.psz_path
) < 0 )
639 if ( ftp_RecvCommand( p_this
, p_sys
, NULL
, &psz_arg
) == 2 )
641 p_sys
->size
= atoll( &psz_arg
[4] );
643 msg_Dbg( p_access
, "file size: %"PRIu64
, p_sys
->size
);
646 if( ftp_SendCommand( p_this
, p_sys
, "CWD %s", p_sys
->url
.psz_path
) < 0 )
649 if( ftp_RecvCommand( p_this
, p_sys
, NULL
, NULL
) != 2 )
651 msg_Err( p_this
, "file or directory does not exist" );
655 p_sys
->directory
= true;
657 /* Start the 'stream' */
658 if( ftp_StartStream( p_this
, p_sys
, 0 ) < 0 )
660 msg_Err( p_this
, "cannot retrieve file" );
661 clearCmdTLS( p_sys
);
662 net_Close( p_sys
->cmd
.fd
);
669 clearCmdTLS( p_sys
);
670 net_Close( p_sys
->cmd
.fd
);
673 vlc_UrlClean( &p_sys
->url
);
679 static int OutOpen( vlc_object_t
*p_this
)
681 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
684 p_sys
= calloc( 1, sizeof( *p_sys
) );
691 readTLSMode( p_sys
, p_access
->psz_access
);
693 if( parseURL( &p_sys
->url
, p_access
->psz_path
, p_sys
->tlsmode
) )
695 if( p_sys
->url
.psz_path
== NULL
)
697 msg_Err( p_this
, "no filename specified" );
701 if( Connect( p_this
, p_sys
) )
704 /* Start the 'stream' */
705 if( ftp_StartStream( p_this
, p_sys
, 0 ) < 0 )
707 msg_Err( p_access
, "cannot store file" );
708 clearCmdTLS( p_sys
);
709 net_Close( p_sys
->cmd
.fd
);
713 p_access
->pf_seek
= OutSeek
;
714 p_access
->pf_write
= Write
;
715 p_access
->p_sys
= (void *)p_sys
;
720 vlc_UrlClean( &p_sys
->url
);
726 /*****************************************************************************
727 * Close: free unused data structures
728 *****************************************************************************/
729 static void Close( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
731 msg_Dbg( p_access
, "stopping stream" );
732 ftp_StopStream( p_access
, p_sys
);
734 if( ftp_SendCommand( p_access
, p_sys
, "QUIT" ) < 0 )
736 msg_Warn( p_access
, "cannot quit" );
740 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
743 clearCmdTLS( p_sys
);
744 net_Close( p_sys
->cmd
.fd
);
747 vlc_UrlClean( &p_sys
->url
);
751 static void InClose( vlc_object_t
*p_this
)
753 Close( p_this
, ((access_t
*)p_this
)->p_sys
);
757 static void OutClose( vlc_object_t
*p_this
)
759 Close( p_this
, GET_OUT_SYS(p_this
));
764 /*****************************************************************************
765 * Seek: try to go at the right place
766 *****************************************************************************/
767 static int _Seek( vlc_object_t
*p_access
, access_sys_t
*p_sys
, uint64_t i_pos
)
769 msg_Dbg( p_access
, "seeking to %"PRIu64
, i_pos
);
771 ftp_StopStream( (vlc_object_t
*)p_access
, p_sys
);
772 if( ftp_StartStream( (vlc_object_t
*)p_access
, p_sys
, i_pos
) < 0 )
778 static int Seek( access_t
*p_access
, uint64_t i_pos
)
780 int val
= _Seek( (vlc_object_t
*)p_access
, p_access
->p_sys
, i_pos
);
784 p_access
->info
.b_eof
= false;
785 p_access
->info
.i_pos
= i_pos
;
791 static int OutSeek( sout_access_out_t
*p_access
, off_t i_pos
)
793 return _Seek( (vlc_object_t
*)p_access
, GET_OUT_SYS( p_access
), i_pos
);
797 /*****************************************************************************
799 *****************************************************************************/
800 static ssize_t
Read( access_t
*p_access
, uint8_t *p_buffer
, size_t i_len
)
802 access_sys_t
*p_sys
= p_access
->p_sys
;
804 assert( p_sys
->data
.fd
!= -1 );
805 assert( !p_sys
->out
);
807 if( p_access
->info
.b_eof
)
810 if( p_sys
->directory
)
812 char *psz_line
= net_Gets( p_access
, p_sys
->data
.fd
, p_sys
->data
.p_vs
);
815 p_access
->info
.b_eof
= true;
820 snprintf( (char*)p_buffer
, i_len
, "%s://%s:%d/%s/%s\n",
821 ( p_sys
->tlsmode
== NONE
) ? "ftp" :
822 ( ( p_sys
->tlsmode
== IMPLICIT
) ? "ftps" : "ftpes" ),
823 p_sys
->url
.psz_host
, p_sys
->url
.i_port
,
824 p_sys
->url
.psz_path
, psz_line
);
826 return strlen( (const char *)p_buffer
);
831 int i_read
= net_Read( p_access
, p_sys
->data
.fd
, p_sys
->data
.p_vs
,
832 p_buffer
, i_len
, false );
834 p_access
->info
.b_eof
= true;
835 else if( i_read
> 0 )
836 p_access
->info
.i_pos
+= i_read
;
842 /*****************************************************************************
844 *****************************************************************************/
846 static ssize_t
Write( sout_access_out_t
*p_access
, block_t
*p_buffer
)
848 access_sys_t
*p_sys
= GET_OUT_SYS(p_access
);
851 assert( p_sys
->data
.fd
!= -1 );
853 while( p_buffer
!= NULL
)
855 block_t
*p_next
= p_buffer
->p_next
;;
857 i_write
+= net_Write( p_access
, p_sys
->data
.fd
, p_sys
->data
.p_vs
,
858 p_buffer
->p_buffer
, p_buffer
->i_buffer
);
859 block_Release( p_buffer
);
868 /*****************************************************************************
870 *****************************************************************************/
871 static int Control( access_t
*p_access
, int i_query
, va_list args
)
878 case ACCESS_CAN_SEEK
:
879 pb_bool
= (bool*)va_arg( args
, bool* );
880 *pb_bool
= !p_access
->p_sys
->directory
;
882 case ACCESS_CAN_FASTSEEK
:
883 pb_bool
= (bool*)va_arg( args
, bool* );
886 case ACCESS_CAN_PAUSE
:
887 pb_bool
= (bool*)va_arg( args
, bool* );
888 *pb_bool
= true; /* FIXME */
890 case ACCESS_CAN_CONTROL_PACE
:
891 pb_bool
= (bool*)va_arg( args
, bool* );
892 *pb_bool
= true; /* FIXME */
894 case ACCESS_GET_SIZE
:
895 *va_arg( args
, uint64_t * ) = p_access
->p_sys
->size
;
898 case ACCESS_GET_PTS_DELAY
:
899 pi_64
= (int64_t*)va_arg( args
, int64_t * );
900 *pi_64
= INT64_C(1000)
901 * var_InheritInteger( p_access
, "network-caching" );
904 case ACCESS_SET_PAUSE_STATE
:
905 pb_bool
= (bool*)va_arg( args
, bool* );
907 return Seek( p_access
, p_access
->info
.i_pos
);
917 static int ftp_StartStream( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
920 char psz_ipv4
[16], *psz_ip
= p_sys
->sz_epsv_ip
;
922 char *psz_arg
, *psz_parser
;
925 assert( p_sys
->data
.fd
== -1 );
927 if( ( ftp_SendCommand( p_access
, p_sys
, *psz_ip
? "EPSV" : "PASV" ) < 0 )
928 || ( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, &psz_arg
) != 2 ) )
930 msg_Err( p_access
, "cannot set passive mode" );
934 psz_parser
= strchr( psz_arg
, '(' );
935 if( psz_parser
== NULL
)
938 msg_Err( p_access
, "cannot parse passive mode response" );
944 char psz_fmt
[7] = "(|||%u";
945 psz_fmt
[1] = psz_fmt
[2] = psz_fmt
[3] = psz_parser
[1];
947 if( sscanf( psz_parser
, psz_fmt
, &i_port
) < 1 )
950 msg_Err( p_access
, "cannot parse passive mode response" );
956 unsigned a1
, a2
, a3
, a4
, p1
, p2
;
958 if( ( sscanf( psz_parser
, "(%u,%u,%u,%u,%u,%u", &a1
, &a2
, &a3
, &a4
,
959 &p1
, &p2
) < 6 ) || ( a1
> 255 ) || ( a2
> 255 )
960 || ( a3
> 255 ) || ( a4
> 255 ) || ( p1
> 255 ) || ( p2
> 255 ) )
963 msg_Err( p_access
, "cannot parse passive mode response" );
967 sprintf( psz_ipv4
, "%u.%u.%u.%u", a1
, a2
, a3
, a4
);
969 i_port
= (p1
<< 8) | p2
;
973 msg_Dbg( p_access
, "ip:%s port:%d", psz_ip
, i_port
);
975 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
976 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) != 2 )
978 msg_Err( p_access
, "cannot set binary transfer mode" );
984 if( ftp_SendCommand( p_access
, p_sys
, "REST %"PRIu64
, i_start
) < 0 ||
985 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) > 3 )
987 msg_Err( p_access
, "cannot set restart offset" );
992 msg_Dbg( p_access
, "waiting for data connection..." );
993 p_sys
->data
.fd
= net_ConnectTCP( p_access
, psz_ip
, i_port
);
994 if( p_sys
->data
.fd
< 0 )
996 msg_Err( p_access
, "failed to connect with server" );
999 msg_Dbg( p_access
, "connection with \"%s:%d\" successful",
1002 if( p_sys
->directory
)
1004 if( ftp_SendCommand( p_access
, p_sys
, "NLST" ) < 0 ||
1005 ftp_RecvCommand( p_access
, p_sys
, NULL
, &psz_arg
) > 2 )
1007 msg_Err( p_access
, "cannot list directory contents" );
1008 return VLC_EGENERIC
;
1014 assert( p_sys
->url
.psz_path
);
1015 if( ftp_SendCommand( p_access
, p_sys
, "%s %s",
1016 p_sys
->out
? "STOR" : "RETR",
1017 p_sys
->url
.psz_path
) < 0
1018 || ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) > 2 )
1020 msg_Err( p_access
, "cannot retrieve file" );
1021 return VLC_EGENERIC
;
1025 if( p_sys
->tlsmode
!= NONE
)
1027 /* FIXME: Do Reuse TLS Session */
1028 /* TLS/SSL handshake */
1029 p_sys
->data
.p_tls
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
,
1030 p_sys
->data
.fd
, p_sys
->url
.psz_host
,
1031 ( p_sys
->tlsmode
== EXPLICIT
) ? "ftpes-data"
1034 if( p_sys
->data
.p_tls
== NULL
)
1036 msg_Err( p_access
, "cannot establish FTP/TLS session for data" \
1037 ": server not allowing new session ?" );
1038 return VLC_EGENERIC
;
1040 p_sys
->data
.p_vs
= &p_sys
->data
.p_tls
->sock
;
1043 shutdown( p_sys
->data
.fd
, p_sys
->out
? SHUT_RD
: SHUT_WR
);
1048 static int ftp_StopStream ( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
1050 if( ftp_SendCommand( p_access
, p_sys
, "ABOR" ) < 0 )
1052 msg_Warn( p_access
, "cannot abort file" );
1053 if( p_sys
->data
.fd
> 0 )
1055 if ( p_sys
->data
.p_tls
) vlc_tls_SessionDelete( p_sys
->data
.p_tls
);
1056 net_Close( p_sys
->data
.fd
);
1058 p_sys
->data
.fd
= -1;
1059 p_sys
->data
.p_tls
= NULL
;
1060 p_sys
->data
.p_vs
= NULL
;
1061 return VLC_EGENERIC
;
1064 if( p_sys
->data
.fd
!= -1 )
1066 if ( p_sys
->data
.p_tls
) vlc_tls_SessionDelete( p_sys
->data
.p_tls
);
1067 net_Close( p_sys
->data
.fd
);
1068 p_sys
->data
.fd
= -1;
1069 p_sys
->data
.p_tls
= NULL
;
1070 p_sys
->data
.p_vs
= NULL
;
1071 /* Read the final response from RETR/STOR, i.e. 426 or 226 */
1072 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
1074 /* Read the response from ABOR, i.e. 226 or 225 */
1075 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);