1 /*****************************************************************************
2 * ftp.c: FTP input module
3 *****************************************************************************
4 * Copyright (C) 2001-2006 VLC authors and VideoLAN
5 * Copyright © 2006 Rémi Denis-Courmont
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr> - original code
8 * Rémi Denis-Courmont - EPSV support
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
36 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38 #include <vlc_access.h>
39 #include <vlc_dialog.h>
40 #include <vlc_input_item.h>
41 #include <vlc_network.h>
45 #include <vlc_charset.h>
46 #include <vlc_interrupt.h>
47 #include <vlc_keystore.h>
50 # define IPPORT_FTP 21u
54 # define IPPORT_FTPS 990u
57 /*****************************************************************************
59 *****************************************************************************/
60 static int InOpen ( vlc_object_t
* );
61 static void InClose( vlc_object_t
* );
63 static int OutOpen ( vlc_object_t
* );
64 static void OutClose( vlc_object_t
* );
67 #define USER_TEXT N_("Username")
68 #define USER_LONGTEXT N_("Username that will be used for the connection, " \
69 "if no username is set in the URL.")
70 #define PASS_TEXT N_("Password")
71 #define PASS_LONGTEXT N_("Password that will be used for the connection, " \
72 "if no username or password are set in URL.")
73 #define ACCOUNT_TEXT N_("FTP account")
74 #define ACCOUNT_LONGTEXT N_("Account that will be " \
75 "used for the connection.")
77 #define LOGIN_DIALOG_TITLE _("FTP authentication")
78 #define LOGIN_DIALOG_TEXT _("Please enter a valid login and password for " \
79 "the ftp connexion to %s")
82 set_shortname( "FTP" )
83 set_description( N_("FTP input") )
84 set_capability( "access", 0 )
85 set_category( CAT_INPUT
)
86 set_subcategory( SUBCAT_INPUT_ACCESS
)
87 add_string( "ftp-user", NULL
, USER_TEXT
, USER_LONGTEXT
, false )
88 add_password( "ftp-pwd", NULL
, PASS_TEXT
, PASS_LONGTEXT
)
89 add_string( "ftp-account", "anonymous", ACCOUNT_TEXT
,
90 ACCOUNT_LONGTEXT
, false )
91 add_shortcut( "ftp", "ftps", "ftpes" )
92 set_callbacks( InOpen
, InClose
)
96 set_shortname( "FTP" )
97 set_description( N_("FTP upload output") )
98 set_capability( "sout access", 0 )
99 set_category( CAT_SOUT
)
100 set_subcategory( SUBCAT_SOUT_ACO
)
101 add_shortcut( "ftp", "ftps", "ftpes" )
102 set_callbacks( OutOpen
, OutClose
)
106 /*****************************************************************************
108 *****************************************************************************/
110 typedef struct access_sys_t access_sys_t
;
112 static ssize_t
Read( stream_t
*, void *, size_t );
113 static int Seek( stream_t
*, uint64_t );
114 static int Control( stream_t
*, int, va_list );
115 static int DirRead( stream_t
*, input_item_node_t
* );
117 static int OutSeek( sout_access_out_t
*, off_t
);
118 static ssize_t
Write( sout_access_out_t
*, block_t
* );
121 static int LoginUserPwd( vlc_object_t
*, access_sys_t
*,
122 const char *, const char *, bool * );
123 static void FeaturesCheck( void *, const char * );
125 typedef struct ftp_features_t
143 ftp_features_t features
;
144 vlc_tls_client_t
*p_creds
;
145 enum tls_mode_e tlsmode
;
149 char sz_epsv_ip
[NI_MAXNUMERICHOST
];
154 #define GET_OUT_SYS( p_this ) \
155 ((access_sys_t *)(((sout_access_out_t *)(p_this))->p_sys))
158 static int ftp_SendCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
159 const char *fmt
, ... )
161 size_t fmtlen
= strlen( fmt
);
162 char fmtbuf
[fmtlen
+ 3];
164 memcpy( fmtbuf
, fmt
, fmtlen
);
165 memcpy( fmtbuf
+ fmtlen
, "\r\n", 3 );
171 va_start( args
, fmt
);
172 val
= vasprintf( &cmd
, fmtbuf
, args
);
174 if( unlikely(val
== -1) )
177 if( strncmp( cmd
, "PASS ", 5 ) && strncmp( cmd
, "ACCT ", 5 ) )
178 msg_Dbg( obj
, "sending request: \"%.*s\" (%d bytes)", val
-2, cmd
, val
);
180 msg_Dbg( obj
, "sending request: \"%.*s XXXX\" (XX bytes)", 4, cmd
);
182 if( vlc_tls_Write( sys
->cmd
, cmd
, val
) != val
)
184 msg_Err( obj
, "request failure" );
193 static char *ftp_GetLine( vlc_object_t
*obj
, access_sys_t
*sys
)
195 char *resp
= vlc_tls_GetLine( sys
->cmd
);
197 msg_Err( obj
, "response failure" );
201 /* TODO support this s**t :
202 RFC 959 allows the client to send certain TELNET strings at any moment,
203 even in the middle of a request:
206 * \377\376x where x is one byte.
207 * \377\375x where x is one byte. The server is obliged to send \377\374x
208 * immediately after reading x.
209 * \377\374x where x is one byte.
210 * \377\373x where x is one byte. The server is obliged to send \377\376x
211 * immediately after reading x.
212 * \377x for any other byte x.
214 These strings are not part of the requests, except in the case \377\377,
215 where the request contains one \377. */
216 static int ftp_RecvReply( vlc_object_t
*obj
, access_sys_t
*sys
,
217 char **restrict strp
,
218 void (*cb
)(void *, const char *), void *opaque
)
220 char *resp
= ftp_GetLine( obj
, sys
);
225 unsigned code
= strtoul( resp
, &end
, 10 );
226 if( (end
- resp
) != 3 || (*end
!= '-' && *end
!= ' ') )
228 msg_Err( obj
, "malformatted response" );
231 msg_Dbg( obj
, "received response: \"%s\"", resp
);
233 if( *end
== '-' ) /* Multi-line response */
240 char *line
= ftp_GetLine( obj
, sys
);
244 done
= !strncmp( resp
, line
, 4 );
262 static int ftp_RecvAnswer( vlc_object_t
*obj
, access_sys_t
*sys
,
263 int *restrict codep
, char **restrict strp
,
264 void (*cb
)(void *, const char *), void *opaque
)
267 int val
= ftp_RecvReply( obj
, sys
, &str
, cb
, opaque
);
268 if( (val
/ 100) == 1 )
269 { /* There can be zero or one preliminary reply per command */
271 val
= ftp_RecvReply( obj
, sys
, &str
, cb
, opaque
);
294 static void DummyLine( void *data
, const char *str
)
296 (void) data
; (void) str
;
299 static int ftp_RecvCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
300 int *restrict codep
, char **restrict strp
)
302 return ftp_RecvAnswer( obj
, sys
, codep
, strp
, DummyLine
, NULL
);
305 static int ftp_RecvCommandInit( vlc_object_t
*obj
, access_sys_t
*sys
)
307 int val
= ftp_RecvReply( obj
, sys
, NULL
, DummyLine
, NULL
);
313 static int ftp_StartStream( vlc_object_t
*, access_sys_t
*, uint64_t, bool );
314 static int ftp_StopStream ( vlc_object_t
*, access_sys_t
* );
316 static int readTLSMode( vlc_object_t
*obj
, access_sys_t
*p_sys
,
317 const char * psz_access
)
319 if ( !strncmp( psz_access
, "ftps", 4 ) )
320 p_sys
->tlsmode
= IMPLICIT
;
322 if ( !strncmp( psz_access
, "ftpes", 5 ) )
323 p_sys
->tlsmode
= EXPLICIT
;
326 p_sys
->p_creds
= NULL
;
327 p_sys
->tlsmode
= NONE
;
331 p_sys
->p_creds
= vlc_tls_ClientCreate( obj
);
332 return (p_sys
->p_creds
!= NULL
) ? 0 : -1;
335 static int createCmdTLS( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
336 const char *psz_session_name
)
338 /* TLS/SSL handshake */
339 vlc_tls_t
*secure
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
,
346 msg_Err( p_access
, "cannot establish FTP/TLS session on command channel" );
353 static void clearCmd( access_sys_t
*p_sys
)
355 if( p_sys
->cmd
!= NULL
)
357 vlc_tls_Close( p_sys
->cmd
);
362 static int Login( vlc_object_t
*p_access
, access_sys_t
*p_sys
, const char *path
)
366 /* *** Open a TCP connection with server *** */
367 p_sys
->cmd
= vlc_tls_SocketOpenTCP( p_access
, p_sys
->url
.psz_host
,
369 if( p_sys
->cmd
== NULL
)
371 msg_Err( p_access
, "connection failed" );
372 vlc_dialog_display_error( p_access
, _("Network interaction failed"), "%s",
373 _("VLC could not connect with the given server.") );
377 if ( p_sys
->tlsmode
== IMPLICIT
) /* FTPS Mode */
379 if ( createCmdTLS( p_access
, p_sys
, "ftps") < 0 )
383 while( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) == 1 );
385 if( i_answer
/ 100 != 2 )
387 msg_Err( p_access
, "connection rejected" );
388 vlc_dialog_display_error( p_access
, _("Network interaction failed"), "%s",
389 _("VLC's connection to the given server was rejected.") );
393 msg_Dbg( p_access
, "connection accepted (%d)", i_answer
);
395 /* Features check first */
396 if( ftp_SendCommand( p_access
, p_sys
, "FEAT" ) < 0
397 || ftp_RecvAnswer( p_access
, p_sys
, NULL
, NULL
,
398 FeaturesCheck
, &p_sys
->features
) < 0 )
400 msg_Err( p_access
, "cannot get server features" );
404 /* Create TLS Session */
405 if( p_sys
->tlsmode
== EXPLICIT
)
407 if ( ! p_sys
->features
.b_authtls
)
409 msg_Err( p_access
, "Server does not support TLS" );
413 if( ftp_SendCommand( p_access
, p_sys
, "AUTH TLS" ) < 0
414 || ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0
417 msg_Err( p_access
, "cannot switch to TLS: server replied with code %d",
422 if( createCmdTLS( p_access
, p_sys
, "ftpes") < 0 )
428 if( p_sys
->tlsmode
!= NONE
)
430 if( ftp_SendCommand( p_access
, p_sys
, "PBSZ 0" ) < 0 ||
431 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
434 msg_Err( p_access
, "Can't truncate Protection buffer size for TLS" );
438 if( ftp_SendCommand( p_access
, p_sys
, "PROT P" ) < 0 ||
439 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
442 msg_Err( p_access
, "Can't set Data channel protection" );
448 vlc_credential credential
;
449 if( vlc_UrlParseFixup( &url
, path
) != 0 )
451 vlc_UrlClean( &url
);
454 vlc_credential_init( &credential
, &url
);
455 bool b_logged
= false;
457 /* First: try credentials from url / option */
458 vlc_credential_get( &credential
, p_access
, "ftp-user", "ftp-pwd",
462 const char *psz_username
= credential
.psz_username
;
464 if( psz_username
== NULL
) /* use anonymous by default */
465 psz_username
= "anonymous";
467 if( LoginUserPwd( p_access
, p_sys
, psz_username
,
468 credential
.psz_password
, &b_logged
) != 0
472 while( vlc_credential_get( &credential
, p_access
, "ftp-user", "ftp-pwd",
473 LOGIN_DIALOG_TITLE
, LOGIN_DIALOG_TEXT
,
478 vlc_credential_store( &credential
, p_access
);
479 vlc_credential_clean( &credential
);
480 vlc_UrlClean( &url
);
483 vlc_credential_clean( &credential
);
484 vlc_UrlClean( &url
);
490 static int LoginUserPwd( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
491 const char *psz_user
, const char *psz_pwd
,
496 /* Send credentials over channel */
497 if( ftp_SendCommand( p_access
, p_sys
, "USER %s", psz_user
) < 0 ||
498 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
501 switch( i_answer
/ 100 )
504 /* X.509 auth successful after AUTH TLS / RFC 2228 sec. 4 */
505 if ( i_answer
== 232 )
506 msg_Dbg( p_access
, "user accepted and authenticated" );
508 msg_Dbg( p_access
, "user accepted" );
511 msg_Dbg( p_access
, "password needed" );
513 if( ftp_SendCommand( p_access
, p_sys
, "PASS %s", psz_pwd
) < 0 ||
514 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
517 switch( i_answer
/ 100 )
520 msg_Dbg( p_access
, "password accepted" );
525 msg_Dbg( p_access
, "account needed" );
526 psz
= var_InheritString( p_access
, "ftp-account" );
527 if( ftp_SendCommand( p_access
, p_sys
, "ACCT %s",
529 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
536 if( i_answer
/ 100 != 2 )
538 msg_Err( p_access
, "account rejected" );
539 vlc_dialog_display_error( p_access
,
540 _("Network interaction failed"),
541 "%s", _("Your account was rejected.") );
544 msg_Dbg( p_access
, "account accepted" );
549 msg_Warn( p_access
, "password rejected" );
555 msg_Warn( p_access
, "user rejected" );
564 static void FeaturesCheck( void *opaque
, const char *feature
)
566 ftp_features_t
*features
= opaque
;
568 if( strcasestr( feature
, "UTF8" ) != NULL
)
569 features
->b_unicode
= true;
571 if( strcasestr( feature
, "AUTH TLS" ) != NULL
)
572 features
->b_authtls
= true;
574 if( strcasestr( feature
, "MLST" ) != NULL
)
575 features
->b_mlst
= true;
578 static int Connect( vlc_object_t
*p_access
, access_sys_t
*p_sys
, const char *path
)
580 if( Login( p_access
, p_sys
, path
) < 0 )
583 /* Extended passive mode */
584 if( ftp_SendCommand( p_access
, p_sys
, "EPSV ALL" ) < 0 )
586 msg_Err( p_access
, "cannot request extended passive mode" );
590 if( ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) == 2 )
592 int fd
= vlc_tls_GetFD(p_sys
->cmd
);
593 if( net_GetPeerAddress( fd
, p_sys
->sz_epsv_ip
, NULL
) )
598 /* If ESPV ALL fails, we fallback to PASV.
599 * We have to restart the connection in case there is a NAT that
600 * understands EPSV ALL in the way, and hence won't allow PASV on
601 * the initial connection.
603 msg_Info( p_access
, "FTP Extended passive mode disabled" );
606 if( Login( p_access
, p_sys
, path
) )
610 if( p_sys
->url
.psz_path
&&
611 (p_sys
->features
.b_unicode
? IsUTF8
: IsASCII
)(p_sys
->url
.psz_path
) == NULL
)
613 msg_Err( p_access
, "unsupported path: \"%s\"", p_sys
->url
.psz_path
);
617 /* check binary mode support */
618 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
619 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) != 2 )
621 msg_Err( p_access
, "cannot set binary transfer mode" );
633 static int parseURL( vlc_url_t
*url
, const char *path
, enum tls_mode_e mode
)
638 /* *** Parse URL and get server addr/port and path *** */
639 while( *path
== '/' )
642 vlc_UrlParseFixup( url
, path
);
644 if( url
->psz_host
== NULL
|| *url
->psz_host
== '\0' )
647 if( url
->i_port
<= 0 )
649 if( mode
== IMPLICIT
)
650 url
->i_port
= IPPORT_FTPS
;
652 url
->i_port
= IPPORT_FTP
; /* default port */
655 if( url
->psz_path
== NULL
)
657 /* FTP URLs are relative to user's default directory (RFC1738 §3.2)
658 For absolute path use ftp://foo.bar//usr/local/etc/filename */
659 /* FIXME: we should issue a series of CWD, one per slash */
662 assert( url
->psz_path
[0] == '/' );
666 char *type
= strstr( url
->psz_path
, ";type=" );
670 if( strchr( "iI", type
[6] ) == NULL
)
671 return VLC_EGENERIC
; /* ASCII and directory not supported */
673 vlc_uri_decode( url
->psz_path
);
678 /****************************************************************************
679 * Open: connect to ftp server and ask for file
680 ****************************************************************************/
681 static int InOpen( vlc_object_t
*p_this
)
683 stream_t
*p_access
= (stream_t
*)p_this
;
689 p_sys
= p_access
->p_sys
= (access_sys_t
*)vlc_obj_calloc( p_this
, 1, sizeof( access_sys_t
) );
695 p_sys
->size
= UINT64_MAX
;
697 if( readTLSMode( p_this
, p_sys
, p_access
->psz_name
) )
700 if( parseURL( &p_sys
->url
, p_access
->psz_url
, p_sys
->tlsmode
) )
703 if( Connect( p_this
, p_sys
, p_access
->psz_url
) )
708 if( p_sys
->url
.psz_path
== NULL
|| !*p_sys
->url
.psz_path
)
714 if( ftp_SendCommand( p_this
, p_sys
, "SIZE %s",
715 p_sys
->url
.psz_path
) < 0 )
718 int val
= ftp_RecvCommand( p_this
, p_sys
, NULL
, &psz_arg
);
722 p_sys
->size
= atoll( &psz_arg
[4] );
724 msg_Dbg( p_access
, "file size: %"PRIu64
, p_sys
->size
);
730 if( ftp_SendCommand( p_this
, p_sys
, "CWD %s",
731 p_sys
->url
.psz_path
) < 0 )
734 if( ftp_RecvCommand( p_this
, p_sys
, NULL
, NULL
) == 2 )
740 msg_Err( p_this
, "file or directory does not exist" );
746 p_access
->pf_readdir
= DirRead
;
747 p_access
->pf_control
= access_vaDirectoryControlHelper
;
749 ACCESS_SET_CALLBACKS( Read
, NULL
, Control
, Seek
); \
751 /* Start the 'stream' */
752 if( ftp_StartStream( p_this
, p_sys
, 0, b_directory
) < 0 )
754 msg_Err( p_this
, "cannot retrieve file" );
764 vlc_UrlClean( &p_sys
->url
);
765 vlc_tls_ClientDelete( p_sys
->p_creds
);
770 static int OutOpen( vlc_object_t
*p_this
)
772 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
775 p_sys
= vlc_obj_calloc( p_this
, 1, sizeof( *p_sys
) );
783 if( readTLSMode( p_this
, p_sys
, p_access
->psz_access
) )
786 if( parseURL( &p_sys
->url
, p_access
->psz_path
, p_sys
->tlsmode
) )
788 if( p_sys
->url
.psz_path
== NULL
)
790 msg_Err( p_this
, "no filename specified" );
794 if( Connect( p_this
, p_sys
, p_access
->psz_path
) )
797 /* Start the 'stream' */
798 if( ftp_StartStream( p_this
, p_sys
, 0, false ) < 0 )
800 msg_Err( p_access
, "cannot store file" );
805 p_access
->pf_seek
= OutSeek
;
806 p_access
->pf_write
= Write
;
807 p_access
->p_sys
= (void *)p_sys
;
812 vlc_UrlClean( &p_sys
->url
);
813 vlc_tls_ClientDelete( p_sys
->p_creds
);
818 /*****************************************************************************
819 * Close: free unused data structures
820 *****************************************************************************/
821 static void Close( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
823 msg_Dbg( p_access
, "stopping stream" );
824 ftp_StopStream( p_access
, p_sys
);
826 if( ftp_SendCommand( p_access
, p_sys
, "QUIT" ) < 0 )
828 msg_Warn( p_access
, "cannot quit" );
832 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
838 vlc_UrlClean( &p_sys
->url
);
839 vlc_tls_ClientDelete( p_sys
->p_creds
);
842 static void InClose( vlc_object_t
*p_this
)
844 Close( p_this
, ((stream_t
*)p_this
)->p_sys
);
848 static void OutClose( vlc_object_t
*p_this
)
850 Close( p_this
, GET_OUT_SYS(p_this
));
855 /*****************************************************************************
856 * Seek: try to go at the right place
857 *****************************************************************************/
858 static int SeekCommon( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
861 msg_Dbg( p_access
, "seeking to %"PRIu64
, i_pos
);
863 ftp_StopStream( p_access
, p_sys
);
865 if( ftp_StartStream( p_access
, p_sys
, i_pos
, false ) < 0 )
870 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
872 access_sys_t
*p_sys
= p_access
->p_sys
;
874 int val
= SeekCommon( (vlc_object_t
*)p_access
, p_sys
, i_pos
);
878 p_sys
->offset
= i_pos
;
884 static int OutSeek( sout_access_out_t
*p_access
, off_t i_pos
)
886 return SeekCommon((vlc_object_t
*)p_access
, GET_OUT_SYS(p_access
), i_pos
);
890 /*****************************************************************************
892 *****************************************************************************/
893 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
895 access_sys_t
*p_sys
= p_access
->p_sys
;
897 if( p_sys
->data
== NULL
)
899 assert( !p_sys
->out
);
901 ssize_t i_read
= vlc_tls_Read( p_sys
->data
, p_buffer
, i_len
, false );
903 p_sys
->offset
+= i_read
;
904 else if( errno
!= EINTR
&& errno
!= EAGAIN
)
906 msg_Err( p_access
, "receive error: %s", vlc_strerror_c(errno
) );
913 /*****************************************************************************
915 *****************************************************************************/
916 static int DirRead (stream_t
*p_access
, input_item_node_t
*p_current_node
)
918 access_sys_t
*p_sys
= p_access
->p_sys
;
919 int i_ret
= VLC_SUCCESS
;
921 assert( p_sys
->data
!= NULL
);
922 assert( !p_sys
->out
);
924 struct vlc_readdir_helper rdh
;
925 vlc_readdir_helper_init( &rdh
, p_access
, p_current_node
);
927 while (i_ret
== VLC_SUCCESS
)
930 int type
= ITEM_TYPE_UNKNOWN
;
932 char *psz_line
= vlc_tls_GetLine( p_sys
->data
);
933 if( psz_line
== NULL
)
936 if( p_sys
->features
.b_mlst
)
938 /* MLST Format is key=val;key=val...; FILENAME */
939 if( strstr( psz_line
, "type=dir" ) )
940 type
= ITEM_TYPE_DIRECTORY
;
941 if( strstr( psz_line
, "type=file" ) )
942 type
= ITEM_TYPE_FILE
;
944 /* Get the filename or fail */
945 psz_file
= strchr( psz_line
, ' ' );
950 msg_Warn( p_access
, "Empty filename in MLST list" );
959 char *psz_filename
= vlc_uri_encode( psz_file
);
960 if( psz_filename
!= NULL
&&
961 asprintf( &psz_uri
, "%s://%s:%d%s%s/%s",
962 ( p_sys
->tlsmode
== NONE
) ? "ftp" :
963 ( ( p_sys
->tlsmode
== IMPLICIT
) ? "ftps" : "ftpes" ),
964 p_sys
->url
.psz_host
, p_sys
->url
.i_port
,
965 p_sys
->url
.psz_path
? "/" : "",
966 p_sys
->url
.psz_path
? p_sys
->url
.psz_path
: "",
967 psz_filename
) != -1 )
969 i_ret
= vlc_readdir_helper_additem( &rdh
, psz_uri
, NULL
, psz_file
,
973 free( psz_filename
);
977 vlc_readdir_helper_finish( &rdh
, i_ret
== VLC_SUCCESS
);
981 /*****************************************************************************
983 *****************************************************************************/
985 static ssize_t
Write( sout_access_out_t
*p_access
, block_t
*p_buffer
)
987 access_sys_t
*p_sys
= GET_OUT_SYS(p_access
);
990 assert( p_sys
->data
!= NULL
);
992 while( p_buffer
!= NULL
)
994 block_t
*p_next
= p_buffer
->p_next
;
996 i_write
+= vlc_tls_Write( p_sys
->data
,
997 p_buffer
->p_buffer
, p_buffer
->i_buffer
);
998 block_Release( p_buffer
);
1007 /*****************************************************************************
1009 *****************************************************************************/
1010 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
1012 access_sys_t
*sys
= p_access
->p_sys
;
1017 case STREAM_CAN_SEEK
:
1018 pb_bool
= va_arg( args
, bool * );
1021 case STREAM_CAN_FASTSEEK
:
1022 pb_bool
= va_arg( args
, bool * );
1025 case STREAM_CAN_PAUSE
:
1026 pb_bool
= va_arg( args
, bool * );
1027 *pb_bool
= true; /* FIXME */
1029 case STREAM_CAN_CONTROL_PACE
:
1030 pb_bool
= va_arg( args
, bool * );
1031 *pb_bool
= true; /* FIXME */
1033 case STREAM_GET_SIZE
:
1034 if( sys
->size
== UINT64_MAX
)
1035 return VLC_EGENERIC
;
1036 *va_arg( args
, uint64_t * ) = sys
->size
;
1039 case STREAM_GET_PTS_DELAY
:
1040 *va_arg( args
, vlc_tick_t
* ) =
1041 VLC_TICK_FROM_MS(var_InheritInteger( p_access
, "network-caching" ));
1044 case STREAM_SET_PAUSE_STATE
:
1045 pb_bool
= va_arg( args
, bool * );
1047 return Seek( p_access
, sys
->offset
);
1051 return VLC_EGENERIC
;
1057 static int ftp_StartStream( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
1058 uint64_t i_start
, bool b_directory
)
1060 char psz_ipv4
[16], *psz_ip
= p_sys
->sz_epsv_ip
;
1062 char *psz_arg
, *psz_parser
;
1065 assert( p_sys
->data
== NULL
);
1067 if( ( ftp_SendCommand( p_access
, p_sys
, *psz_ip
? "EPSV" : "PASV" ) < 0 )
1068 || ( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, &psz_arg
) != 2 ) )
1070 msg_Err( p_access
, "cannot set passive mode" );
1071 return VLC_EGENERIC
;
1074 psz_parser
= strchr( psz_arg
, '(' );
1075 if( psz_parser
== NULL
)
1078 msg_Err( p_access
, "cannot parse passive mode response" );
1079 return VLC_EGENERIC
;
1084 if( sscanf( psz_parser
, "(%*3c%u", &i_port
) < 1 )
1087 msg_Err( p_access
, "cannot parse passive mode response" );
1088 return VLC_EGENERIC
;
1093 unsigned a1
, a2
, a3
, a4
, p1
, p2
;
1095 if( ( sscanf( psz_parser
, "(%u,%u,%u,%u,%u,%u", &a1
, &a2
, &a3
, &a4
,
1096 &p1
, &p2
) < 6 ) || ( a1
> 255 ) || ( a2
> 255 )
1097 || ( a3
> 255 ) || ( a4
> 255 ) || ( p1
> 255 ) || ( p2
> 255 ) )
1100 msg_Err( p_access
, "cannot parse passive mode response" );
1101 return VLC_EGENERIC
;
1104 sprintf( psz_ipv4
, "%u.%u.%u.%u", a1
, a2
, a3
, a4
);
1106 i_port
= (p1
<< 8) | p2
;
1110 msg_Dbg( p_access
, "ip:%s port:%d", psz_ip
, i_port
);
1112 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
1113 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) != 2 )
1115 msg_Err( p_access
, "cannot set binary transfer mode" );
1116 return VLC_EGENERIC
;
1121 if( ftp_SendCommand( p_access
, p_sys
, "REST %"PRIu64
, i_start
) < 0 ||
1122 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) > 3 )
1124 msg_Err( p_access
, "cannot set restart offset" );
1125 return VLC_EGENERIC
;
1129 msg_Dbg( p_access
, "waiting for data connection..." );
1130 p_sys
->data
= vlc_tls_SocketOpenTCP( p_access
, psz_ip
, i_port
);
1131 if( p_sys
->data
== NULL
)
1133 msg_Err( p_access
, "failed to connect with server" );
1134 return VLC_EGENERIC
;
1136 msg_Dbg( p_access
, "connection with \"%s:%d\" successful",
1141 if( p_sys
->features
.b_mlst
&&
1142 ftp_SendCommand( p_access
, p_sys
, "MLSD" ) >= 0 &&
1143 ftp_RecvCommandInit( p_access
, p_sys
) == 1 )
1145 msg_Dbg( p_access
, "Using MLST extension to list" );
1148 if( ftp_SendCommand( p_access
, p_sys
, "NLST" ) < 0 ||
1149 ftp_RecvCommandInit( p_access
, p_sys
) == 1 )
1151 msg_Err( p_access
, "cannot list directory contents" );
1152 return VLC_EGENERIC
;
1158 assert( p_sys
->url
.psz_path
);
1159 if( ftp_SendCommand( p_access
, p_sys
, "%s %s",
1160 p_sys
->out
? "STOR" : "RETR",
1161 p_sys
->url
.psz_path
) < 0
1162 || ftp_RecvCommandInit( p_access
, p_sys
) != 1 )
1164 msg_Err( p_access
, "cannot retrieve file" );
1165 return VLC_EGENERIC
;
1169 if( p_sys
->tlsmode
!= NONE
)
1171 /* FIXME: Do Reuse TLS Session */
1172 /* TLS/SSL handshake */
1173 vlc_tls_t
*secure
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
,
1174 p_sys
->data
, p_sys
->url
.psz_host
,
1175 ( p_sys
->tlsmode
== EXPLICIT
) ? "ftpes-data"
1178 if( secure
== NULL
)
1180 msg_Err( p_access
, "cannot establish FTP/TLS session for data" \
1181 ": server not allowing new session ?" );
1182 return VLC_EGENERIC
;
1184 p_sys
->data
= secure
;
1190 static int ftp_StopStream ( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
1192 int ret
= VLC_SUCCESS
;
1194 if( ftp_SendCommand( p_access
, p_sys
, "ABOR" ) < 0 )
1196 msg_Warn( p_access
, "cannot abort file" );
1200 if( p_sys
->data
!= NULL
)
1202 vlc_tls_Close( p_sys
->data
);
1205 if( ret
== VLC_SUCCESS
)
1206 /* Read the final response from RETR/STOR, i.e. 426 or 226 */
1207 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
1210 if( ret
== VLC_SUCCESS
)
1211 /* Read the response from ABOR, i.e. 226 or 225 */
1212 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);