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 *****************************************************************************/
37 #include <vlc_common.h>
38 #include <vlc_plugin.h>
39 #include <vlc_access.h>
40 #include <vlc_dialog.h>
41 #include <vlc_input_item.h>
42 #include <vlc_network.h>
46 #include <vlc_charset.h>
47 #include <vlc_interrupt.h>
48 #include <vlc_keystore.h>
51 # define IPPORT_FTP 21u
55 # define IPPORT_FTPS 990u
58 /*****************************************************************************
60 *****************************************************************************/
61 static int InOpen ( vlc_object_t
* );
62 static void InClose( vlc_object_t
* );
64 static int OutOpen ( vlc_object_t
* );
65 static void OutClose( vlc_object_t
* );
68 #define USER_TEXT N_("Username")
69 #define USER_LONGTEXT N_("Username that will be used for the connection, " \
70 "if no username is set in the URL.")
71 #define PASS_TEXT N_("Password")
72 #define PASS_LONGTEXT N_("Password that will be used for the connection, " \
73 "if no username or password are set in URL.")
74 #define ACCOUNT_TEXT N_("FTP account")
75 #define ACCOUNT_LONGTEXT N_("Account that will be " \
76 "used for the connection.")
78 #define LOGIN_DIALOG_TITLE _("FTP authentication")
79 #define LOGIN_DIALOG_TEXT _("Please enter a valid login and password for " \
80 "the ftp connexion to %s")
83 set_shortname( "FTP" )
84 set_description( N_("FTP input") )
85 set_capability( "access", 0 )
86 set_category( CAT_INPUT
)
87 set_subcategory( SUBCAT_INPUT_ACCESS
)
88 add_string( "ftp-user", NULL
, USER_TEXT
, USER_LONGTEXT
, false )
89 add_string( "ftp-pwd", NULL
, PASS_TEXT
, PASS_LONGTEXT
, false )
90 add_string( "ftp-account", "anonymous", ACCOUNT_TEXT
,
91 ACCOUNT_LONGTEXT
, false )
92 add_shortcut( "ftp", "ftps", "ftpes" )
93 set_callbacks( InOpen
, InClose
)
97 set_shortname( "FTP" )
98 set_description( N_("FTP upload output") )
99 set_capability( "sout access", 0 )
100 set_category( CAT_SOUT
)
101 set_subcategory( SUBCAT_SOUT_ACO
)
102 add_shortcut( "ftp", "ftps", "ftpes" )
103 set_callbacks( OutOpen
, OutClose
)
107 /*****************************************************************************
109 *****************************************************************************/
111 typedef struct access_sys_t access_sys_t
;
113 static ssize_t
Read( stream_t
*, void *, size_t );
114 static int Seek( stream_t
*, uint64_t );
115 static int Control( stream_t
*, int, va_list );
116 static int DirRead( stream_t
*, input_item_node_t
* );
118 static int OutSeek( sout_access_out_t
*, off_t
);
119 static ssize_t
Write( sout_access_out_t
*, block_t
* );
122 static int LoginUserPwd( vlc_object_t
*, access_sys_t
*,
123 const char *, const char *, bool * );
124 static void FeaturesCheck( void *, const char * );
126 typedef struct ftp_features_t
144 ftp_features_t features
;
145 vlc_tls_creds_t
*p_creds
;
146 enum tls_mode_e tlsmode
;
150 char sz_epsv_ip
[NI_MAXNUMERICHOST
];
155 #define GET_OUT_SYS( p_this ) \
156 ((access_sys_t *)(((sout_access_out_t *)(p_this))->p_sys))
159 static int ftp_SendCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
160 const char *fmt
, ... )
162 size_t fmtlen
= strlen( fmt
);
163 char fmtbuf
[fmtlen
+ 3];
165 memcpy( fmtbuf
, fmt
, fmtlen
);
166 memcpy( fmtbuf
+ fmtlen
, "\r\n", 3 );
172 va_start( args
, fmt
);
173 val
= vasprintf( &cmd
, fmtbuf
, args
);
175 if( unlikely(val
== -1) )
178 if( strncmp( cmd
, "PASS ", 5 ) && strncmp( cmd
, "ACCT ", 5 ) )
179 msg_Dbg( obj
, "sending request: \"%.*s\" (%d bytes)", val
-2, cmd
, val
);
181 msg_Dbg( obj
, "sending request: \"%.*s XXXX\" (XX bytes)", 4, cmd
);
183 if( vlc_tls_Write( sys
->cmd
, cmd
, val
) != val
)
185 msg_Err( obj
, "request failure" );
194 static char *ftp_GetLine( vlc_object_t
*obj
, access_sys_t
*sys
)
196 char *resp
= vlc_tls_GetLine( sys
->cmd
);
198 msg_Err( obj
, "response failure" );
202 /* TODO support this s**t :
203 RFC 959 allows the client to send certain TELNET strings at any moment,
204 even in the middle of a request:
207 * \377\376x where x is one byte.
208 * \377\375x where x is one byte. The server is obliged to send \377\374x
209 * immediately after reading x.
210 * \377\374x where x is one byte.
211 * \377\373x where x is one byte. The server is obliged to send \377\376x
212 * immediately after reading x.
213 * \377x for any other byte x.
215 These strings are not part of the requests, except in the case \377\377,
216 where the request contains one \377. */
217 static int ftp_RecvReply( vlc_object_t
*obj
, access_sys_t
*sys
,
218 char **restrict strp
,
219 void (*cb
)(void *, const char *), void *opaque
)
221 char *resp
= ftp_GetLine( obj
, sys
);
226 unsigned code
= strtoul( resp
, &end
, 10 );
227 if( (end
- resp
) != 3 || (*end
!= '-' && *end
!= ' ') )
229 msg_Err( obj
, "malformatted response" );
232 msg_Dbg( obj
, "received response: \"%s\"", resp
);
234 if( *end
== '-' ) /* Multi-line response */
241 char *line
= ftp_GetLine( obj
, sys
);
245 done
= !strncmp( resp
, line
, 4 );
263 static int ftp_RecvAnswer( vlc_object_t
*obj
, access_sys_t
*sys
,
264 int *restrict codep
, char **restrict strp
,
265 void (*cb
)(void *, const char *), void *opaque
)
268 int val
= ftp_RecvReply( obj
, sys
, &str
, cb
, opaque
);
269 if( (val
/ 100) == 1 )
270 { /* There can be zero or one preliminary reply per command */
272 val
= ftp_RecvReply( obj
, sys
, &str
, cb
, opaque
);
295 static void DummyLine( void *data
, const char *str
)
297 (void) data
; (void) str
;
300 static int ftp_RecvCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
301 int *restrict codep
, char **restrict strp
)
303 return ftp_RecvAnswer( obj
, sys
, codep
, strp
, DummyLine
, NULL
);
306 static int ftp_RecvCommandInit( vlc_object_t
*obj
, access_sys_t
*sys
)
308 int val
= ftp_RecvReply( obj
, sys
, NULL
, DummyLine
, NULL
);
314 static int ftp_StartStream( vlc_object_t
*, access_sys_t
*, uint64_t, bool );
315 static int ftp_StopStream ( vlc_object_t
*, access_sys_t
* );
317 static int readTLSMode( vlc_object_t
*obj
, access_sys_t
*p_sys
,
318 const char * psz_access
)
320 if ( !strncmp( psz_access
, "ftps", 4 ) )
321 p_sys
->tlsmode
= IMPLICIT
;
323 if ( !strncmp( psz_access
, "ftpes", 5 ) )
324 p_sys
->tlsmode
= EXPLICIT
;
327 p_sys
->p_creds
= NULL
;
328 p_sys
->tlsmode
= NONE
;
332 p_sys
->p_creds
= vlc_tls_ClientCreate( obj
);
333 return (p_sys
->p_creds
!= NULL
) ? 0 : -1;
336 static int createCmdTLS( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
337 const char *psz_session_name
)
339 /* TLS/SSL handshake */
340 vlc_tls_t
*secure
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
,
347 msg_Err( p_access
, "cannot establish FTP/TLS session on command channel" );
354 static void clearCmd( access_sys_t
*p_sys
)
356 if( p_sys
->cmd
!= NULL
)
358 vlc_tls_Close( p_sys
->cmd
);
363 static int Login( vlc_object_t
*p_access
, access_sys_t
*p_sys
, const char *path
)
367 /* *** Open a TCP connection with server *** */
368 p_sys
->cmd
= vlc_tls_SocketOpenTCP( p_access
, p_sys
->url
.psz_host
,
370 if( p_sys
->cmd
== NULL
)
372 msg_Err( p_access
, "connection failed" );
373 vlc_dialog_display_error( p_access
, _("Network interaction failed"), "%s",
374 _("VLC could not connect with the given server.") );
378 if ( p_sys
->tlsmode
== IMPLICIT
) /* FTPS Mode */
380 if ( createCmdTLS( p_access
, p_sys
, "ftps") < 0 )
384 while( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) == 1 );
386 if( i_answer
/ 100 != 2 )
388 msg_Err( p_access
, "connection rejected" );
389 vlc_dialog_display_error( p_access
, _("Network interaction failed"), "%s",
390 _("VLC's connection to the given server was rejected.") );
394 msg_Dbg( p_access
, "connection accepted (%d)", i_answer
);
396 /* Features check first */
397 if( ftp_SendCommand( p_access
, p_sys
, "FEAT" ) < 0
398 || ftp_RecvAnswer( p_access
, p_sys
, NULL
, NULL
,
399 FeaturesCheck
, &p_sys
->features
) < 0 )
401 msg_Err( p_access
, "cannot get server features" );
405 /* Create TLS Session */
406 if( p_sys
->tlsmode
== EXPLICIT
)
408 if ( ! p_sys
->features
.b_authtls
)
410 msg_Err( p_access
, "Server does not support TLS" );
414 if( ftp_SendCommand( p_access
, p_sys
, "AUTH TLS" ) < 0
415 || ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0
418 msg_Err( p_access
, "cannot switch to TLS: server replied with code %d",
423 if( createCmdTLS( p_access
, p_sys
, "ftpes") < 0 )
429 if( p_sys
->tlsmode
!= NONE
)
431 if( ftp_SendCommand( p_access
, p_sys
, "PBSZ 0" ) < 0 ||
432 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
435 msg_Err( p_access
, "Can't truncate Protection buffer size for TLS" );
439 if( ftp_SendCommand( p_access
, p_sys
, "PROT P" ) < 0 ||
440 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
443 msg_Err( p_access
, "Can't set Data channel protection" );
449 vlc_credential credential
;
450 if( vlc_UrlParseFixup( &url
, path
) != 0 )
452 vlc_UrlClean( &url
);
455 vlc_credential_init( &credential
, &url
);
456 bool b_logged
= false;
458 /* First: try credentials from url / option */
459 vlc_credential_get( &credential
, p_access
, "ftp-user", "ftp-pwd",
463 const char *psz_username
= credential
.psz_username
;
465 if( psz_username
== NULL
) /* use anonymous by default */
466 psz_username
= "anonymous";
468 if( LoginUserPwd( p_access
, p_sys
, psz_username
,
469 credential
.psz_password
, &b_logged
) != 0
473 while( vlc_credential_get( &credential
, p_access
, "ftp-user", "ftp-pwd",
474 LOGIN_DIALOG_TITLE
, LOGIN_DIALOG_TEXT
,
479 vlc_credential_store( &credential
, p_access
);
480 vlc_credential_clean( &credential
);
481 vlc_UrlClean( &url
);
484 vlc_credential_clean( &credential
);
485 vlc_UrlClean( &url
);
491 static int LoginUserPwd( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
492 const char *psz_user
, const char *psz_pwd
,
497 /* Send credentials over channel */
498 if( ftp_SendCommand( p_access
, p_sys
, "USER %s", psz_user
) < 0 ||
499 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
502 switch( i_answer
/ 100 )
505 /* X.509 auth successful after AUTH TLS / RFC 2228 sec. 4 */
506 if ( i_answer
== 232 )
507 msg_Dbg( p_access
, "user accepted and authenticated" );
509 msg_Dbg( p_access
, "user accepted" );
512 msg_Dbg( p_access
, "password needed" );
514 if( ftp_SendCommand( p_access
, p_sys
, "PASS %s", psz_pwd
) < 0 ||
515 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
518 switch( i_answer
/ 100 )
521 msg_Dbg( p_access
, "password accepted" );
526 msg_Dbg( p_access
, "account needed" );
527 psz
= var_InheritString( p_access
, "ftp-account" );
528 if( ftp_SendCommand( p_access
, p_sys
, "ACCT %s",
530 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
537 if( i_answer
/ 100 != 2 )
539 msg_Err( p_access
, "account rejected" );
540 vlc_dialog_display_error( p_access
,
541 _("Network interaction failed"),
542 "%s", _("Your account was rejected.") );
545 msg_Dbg( p_access
, "account accepted" );
550 msg_Warn( p_access
, "password rejected" );
556 msg_Warn( p_access
, "user rejected" );
565 static void FeaturesCheck( void *opaque
, const char *feature
)
567 ftp_features_t
*features
= opaque
;
569 if( strcasestr( feature
, "UTF8" ) != NULL
)
570 features
->b_unicode
= true;
572 if( strcasestr( feature
, "AUTH TLS" ) != NULL
)
573 features
->b_authtls
= true;
575 if( strcasestr( feature
, "MLST" ) != NULL
)
576 features
->b_mlst
= true;
579 static const char *IsASCII( const char *str
)
582 for( const char *p
= str
; (c
= *p
) != '\0'; p
++ )
588 static int Connect( vlc_object_t
*p_access
, access_sys_t
*p_sys
, const char *path
)
590 if( Login( p_access
, p_sys
, path
) < 0 )
593 /* Extended passive mode */
594 if( ftp_SendCommand( p_access
, p_sys
, "EPSV ALL" ) < 0 )
596 msg_Err( p_access
, "cannot request extended passive mode" );
600 if( ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) == 2 )
602 int fd
= vlc_tls_GetFD(p_sys
->cmd
);
603 if( net_GetPeerAddress( fd
, p_sys
->sz_epsv_ip
, NULL
) )
608 /* If ESPV ALL fails, we fallback to PASV.
609 * We have to restart the connection in case there is a NAT that
610 * understands EPSV ALL in the way, and hence won't allow PASV on
611 * the initial connection.
613 msg_Info( p_access
, "FTP Extended passive mode disabled" );
616 if( Login( p_access
, p_sys
, path
) )
620 if( p_sys
->url
.psz_path
&&
621 (p_sys
->features
.b_unicode
? IsUTF8
: IsASCII
)(p_sys
->url
.psz_path
) == NULL
)
623 msg_Err( p_access
, "unsupported path: \"%s\"", p_sys
->url
.psz_path
);
627 /* check binary mode support */
628 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
629 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) != 2 )
631 msg_Err( p_access
, "cannot set binary transfer mode" );
643 static int parseURL( vlc_url_t
*url
, const char *path
, enum tls_mode_e mode
)
648 /* *** Parse URL and get server addr/port and path *** */
649 while( *path
== '/' )
652 vlc_UrlParseFixup( url
, path
);
654 if( url
->psz_host
== NULL
|| *url
->psz_host
== '\0' )
657 if( url
->i_port
<= 0 )
659 if( mode
== IMPLICIT
)
660 url
->i_port
= IPPORT_FTPS
;
662 url
->i_port
= IPPORT_FTP
; /* default port */
665 if( url
->psz_path
== NULL
)
667 /* FTP URLs are relative to user's default directory (RFC1738 §3.2)
668 For absolute path use ftp://foo.bar//usr/local/etc/filename */
669 /* FIXME: we should issue a series of CWD, one per slash */
672 assert( url
->psz_path
[0] == '/' );
676 char *type
= strstr( url
->psz_path
, ";type=" );
680 if( strchr( "iI", type
[6] ) == NULL
)
681 return VLC_EGENERIC
; /* ASCII and directory not supported */
683 vlc_uri_decode( url
->psz_path
);
688 /****************************************************************************
689 * Open: connect to ftp server and ask for file
690 ****************************************************************************/
691 static int InOpen( vlc_object_t
*p_this
)
693 stream_t
*p_access
= (stream_t
*)p_this
;
699 p_sys
= p_access
->p_sys
= (access_sys_t
*)vlc_obj_calloc( p_this
, 1, sizeof( access_sys_t
) );
705 p_sys
->size
= UINT64_MAX
;
707 if( readTLSMode( p_this
, p_sys
, p_access
->psz_name
) )
710 if( parseURL( &p_sys
->url
, p_access
->psz_url
, p_sys
->tlsmode
) )
713 if( Connect( p_this
, p_sys
, p_access
->psz_url
) )
718 if( p_sys
->url
.psz_path
== NULL
|| !*p_sys
->url
.psz_path
)
724 if( ftp_SendCommand( p_this
, p_sys
, "SIZE %s",
725 p_sys
->url
.psz_path
) < 0 )
728 int val
= ftp_RecvCommand( p_this
, p_sys
, NULL
, &psz_arg
);
732 p_sys
->size
= atoll( &psz_arg
[4] );
734 msg_Dbg( p_access
, "file size: %"PRIu64
, p_sys
->size
);
740 if( ftp_SendCommand( p_this
, p_sys
, "CWD %s",
741 p_sys
->url
.psz_path
) < 0 )
744 if( ftp_RecvCommand( p_this
, p_sys
, NULL
, NULL
) == 2 )
750 msg_Err( p_this
, "file or directory does not exist" );
756 p_access
->pf_readdir
= DirRead
;
757 p_access
->pf_control
= access_vaDirectoryControlHelper
;
759 ACCESS_SET_CALLBACKS( Read
, NULL
, Control
, Seek
); \
761 /* Start the 'stream' */
762 if( ftp_StartStream( p_this
, p_sys
, 0, b_directory
) < 0 )
764 msg_Err( p_this
, "cannot retrieve file" );
774 vlc_UrlClean( &p_sys
->url
);
775 vlc_tls_Delete( p_sys
->p_creds
);
780 static int OutOpen( vlc_object_t
*p_this
)
782 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
785 p_sys
= vlc_obj_calloc( p_this
, 1, sizeof( *p_sys
) );
793 if( readTLSMode( p_this
, p_sys
, p_access
->psz_access
) )
796 if( parseURL( &p_sys
->url
, p_access
->psz_path
, p_sys
->tlsmode
) )
798 if( p_sys
->url
.psz_path
== NULL
)
800 msg_Err( p_this
, "no filename specified" );
804 if( Connect( p_this
, p_sys
, p_access
->psz_path
) )
807 /* Start the 'stream' */
808 if( ftp_StartStream( p_this
, p_sys
, 0, false ) < 0 )
810 msg_Err( p_access
, "cannot store file" );
815 p_access
->pf_seek
= OutSeek
;
816 p_access
->pf_write
= Write
;
817 p_access
->p_sys
= (void *)p_sys
;
822 vlc_UrlClean( &p_sys
->url
);
823 vlc_tls_Delete( p_sys
->p_creds
);
828 /*****************************************************************************
829 * Close: free unused data structures
830 *****************************************************************************/
831 static void Close( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
833 msg_Dbg( p_access
, "stopping stream" );
834 ftp_StopStream( p_access
, p_sys
);
836 if( ftp_SendCommand( p_access
, p_sys
, "QUIT" ) < 0 )
838 msg_Warn( p_access
, "cannot quit" );
842 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
848 vlc_UrlClean( &p_sys
->url
);
849 vlc_tls_Delete( p_sys
->p_creds
);
852 static void InClose( vlc_object_t
*p_this
)
854 Close( p_this
, ((stream_t
*)p_this
)->p_sys
);
858 static void OutClose( vlc_object_t
*p_this
)
860 Close( p_this
, GET_OUT_SYS(p_this
));
865 /*****************************************************************************
866 * Seek: try to go at the right place
867 *****************************************************************************/
868 static int SeekCommon( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
871 msg_Dbg( p_access
, "seeking to %"PRIu64
, i_pos
);
873 ftp_StopStream( p_access
, p_sys
);
875 if( ftp_StartStream( p_access
, p_sys
, i_pos
, false ) < 0 )
880 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
882 access_sys_t
*p_sys
= p_access
->p_sys
;
884 int val
= SeekCommon( (vlc_object_t
*)p_access
, p_sys
, i_pos
);
888 p_sys
->offset
= i_pos
;
894 static int OutSeek( sout_access_out_t
*p_access
, off_t i_pos
)
896 return SeekCommon((vlc_object_t
*)p_access
, GET_OUT_SYS(p_access
), i_pos
);
900 /*****************************************************************************
902 *****************************************************************************/
903 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
905 access_sys_t
*p_sys
= p_access
->p_sys
;
907 if( p_sys
->data
== NULL
)
909 assert( !p_sys
->out
);
911 ssize_t i_read
= vlc_tls_Read( p_sys
->data
, p_buffer
, i_len
, false );
913 p_sys
->offset
+= i_read
;
914 else if( errno
!= EINTR
&& errno
!= EAGAIN
)
916 msg_Err( p_access
, "receive error: %s", vlc_strerror_c(errno
) );
923 /*****************************************************************************
925 *****************************************************************************/
926 static int DirRead (stream_t
*p_access
, input_item_node_t
*p_current_node
)
928 access_sys_t
*p_sys
= p_access
->p_sys
;
929 int i_ret
= VLC_SUCCESS
;
931 assert( p_sys
->data
!= NULL
);
932 assert( !p_sys
->out
);
934 struct vlc_readdir_helper rdh
;
935 vlc_readdir_helper_init( &rdh
, p_access
, p_current_node
);
937 while (i_ret
== VLC_SUCCESS
)
940 int type
= ITEM_TYPE_UNKNOWN
;
942 char *psz_line
= vlc_tls_GetLine( p_sys
->data
);
943 if( psz_line
== NULL
)
946 if( p_sys
->features
.b_mlst
)
948 /* MLST Format is key=val;key=val...; FILENAME */
949 if( strstr( psz_line
, "type=dir" ) )
950 type
= ITEM_TYPE_DIRECTORY
;
951 if( strstr( psz_line
, "type=file" ) )
952 type
= ITEM_TYPE_FILE
;
954 /* Get the filename or fail */
955 psz_file
= strchr( psz_line
, ' ' );
960 msg_Warn( p_access
, "Empty filename in MLST list" );
969 char *psz_filename
= vlc_uri_encode( psz_file
);
970 if( psz_filename
!= NULL
&&
971 asprintf( &psz_uri
, "%s://%s:%d%s%s/%s",
972 ( p_sys
->tlsmode
== NONE
) ? "ftp" :
973 ( ( p_sys
->tlsmode
== IMPLICIT
) ? "ftps" : "ftpes" ),
974 p_sys
->url
.psz_host
, p_sys
->url
.i_port
,
975 p_sys
->url
.psz_path
? "/" : "",
976 p_sys
->url
.psz_path
? p_sys
->url
.psz_path
: "",
977 psz_filename
) != -1 )
979 i_ret
= vlc_readdir_helper_additem( &rdh
, psz_uri
, NULL
, psz_file
,
983 free( psz_filename
);
987 vlc_readdir_helper_finish( &rdh
, i_ret
== VLC_SUCCESS
);
991 /*****************************************************************************
993 *****************************************************************************/
995 static ssize_t
Write( sout_access_out_t
*p_access
, block_t
*p_buffer
)
997 access_sys_t
*p_sys
= GET_OUT_SYS(p_access
);
1000 assert( p_sys
->data
!= NULL
);
1002 while( p_buffer
!= NULL
)
1004 block_t
*p_next
= p_buffer
->p_next
;
1006 i_write
+= vlc_tls_Write( p_sys
->data
,
1007 p_buffer
->p_buffer
, p_buffer
->i_buffer
);
1008 block_Release( p_buffer
);
1017 /*****************************************************************************
1019 *****************************************************************************/
1020 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
1022 access_sys_t
*sys
= p_access
->p_sys
;
1028 case STREAM_CAN_SEEK
:
1029 pb_bool
= va_arg( args
, bool * );
1032 case STREAM_CAN_FASTSEEK
:
1033 pb_bool
= va_arg( args
, bool * );
1036 case STREAM_CAN_PAUSE
:
1037 pb_bool
= va_arg( args
, bool * );
1038 *pb_bool
= true; /* FIXME */
1040 case STREAM_CAN_CONTROL_PACE
:
1041 pb_bool
= va_arg( args
, bool * );
1042 *pb_bool
= true; /* FIXME */
1044 case STREAM_GET_SIZE
:
1045 if( sys
->size
== UINT64_MAX
)
1046 return VLC_EGENERIC
;
1047 *va_arg( args
, uint64_t * ) = sys
->size
;
1050 case STREAM_GET_PTS_DELAY
:
1051 pi_64
= va_arg( args
, int64_t * );
1052 *pi_64
= INT64_C(1000)
1053 * var_InheritInteger( p_access
, "network-caching" );
1056 case STREAM_SET_PAUSE_STATE
:
1057 pb_bool
= va_arg( args
, bool * );
1059 return Seek( p_access
, sys
->offset
);
1063 return VLC_EGENERIC
;
1069 static int ftp_StartStream( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
1070 uint64_t i_start
, bool b_directory
)
1072 char psz_ipv4
[16], *psz_ip
= p_sys
->sz_epsv_ip
;
1074 char *psz_arg
, *psz_parser
;
1077 assert( p_sys
->data
== NULL
);
1079 if( ( ftp_SendCommand( p_access
, p_sys
, *psz_ip
? "EPSV" : "PASV" ) < 0 )
1080 || ( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, &psz_arg
) != 2 ) )
1082 msg_Err( p_access
, "cannot set passive mode" );
1083 return VLC_EGENERIC
;
1086 psz_parser
= strchr( psz_arg
, '(' );
1087 if( psz_parser
== NULL
)
1090 msg_Err( p_access
, "cannot parse passive mode response" );
1091 return VLC_EGENERIC
;
1096 if( sscanf( psz_parser
, "(%*3c%u", &i_port
) < 1 )
1099 msg_Err( p_access
, "cannot parse passive mode response" );
1100 return VLC_EGENERIC
;
1105 unsigned a1
, a2
, a3
, a4
, p1
, p2
;
1107 if( ( sscanf( psz_parser
, "(%u,%u,%u,%u,%u,%u", &a1
, &a2
, &a3
, &a4
,
1108 &p1
, &p2
) < 6 ) || ( a1
> 255 ) || ( a2
> 255 )
1109 || ( a3
> 255 ) || ( a4
> 255 ) || ( p1
> 255 ) || ( p2
> 255 ) )
1112 msg_Err( p_access
, "cannot parse passive mode response" );
1113 return VLC_EGENERIC
;
1116 sprintf( psz_ipv4
, "%u.%u.%u.%u", a1
, a2
, a3
, a4
);
1118 i_port
= (p1
<< 8) | p2
;
1122 msg_Dbg( p_access
, "ip:%s port:%d", psz_ip
, i_port
);
1124 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
1125 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) != 2 )
1127 msg_Err( p_access
, "cannot set binary transfer mode" );
1128 return VLC_EGENERIC
;
1133 if( ftp_SendCommand( p_access
, p_sys
, "REST %"PRIu64
, i_start
) < 0 ||
1134 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) > 3 )
1136 msg_Err( p_access
, "cannot set restart offset" );
1137 return VLC_EGENERIC
;
1141 msg_Dbg( p_access
, "waiting for data connection..." );
1142 p_sys
->data
= vlc_tls_SocketOpenTCP( p_access
, psz_ip
, i_port
);
1143 if( p_sys
->data
== NULL
)
1145 msg_Err( p_access
, "failed to connect with server" );
1146 return VLC_EGENERIC
;
1148 msg_Dbg( p_access
, "connection with \"%s:%d\" successful",
1153 if( p_sys
->features
.b_mlst
&&
1154 ftp_SendCommand( p_access
, p_sys
, "MLSD" ) >= 0 &&
1155 ftp_RecvCommandInit( p_access
, p_sys
) == 1 )
1157 msg_Dbg( p_access
, "Using MLST extension to list" );
1160 if( ftp_SendCommand( p_access
, p_sys
, "NLST" ) < 0 ||
1161 ftp_RecvCommandInit( p_access
, p_sys
) == 1 )
1163 msg_Err( p_access
, "cannot list directory contents" );
1164 return VLC_EGENERIC
;
1170 assert( p_sys
->url
.psz_path
);
1171 if( ftp_SendCommand( p_access
, p_sys
, "%s %s",
1172 p_sys
->out
? "STOR" : "RETR",
1173 p_sys
->url
.psz_path
) < 0
1174 || ftp_RecvCommandInit( p_access
, p_sys
) != 1 )
1176 msg_Err( p_access
, "cannot retrieve file" );
1177 return VLC_EGENERIC
;
1181 if( p_sys
->tlsmode
!= NONE
)
1183 /* FIXME: Do Reuse TLS Session */
1184 /* TLS/SSL handshake */
1185 vlc_tls_t
*secure
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
,
1186 p_sys
->data
, p_sys
->url
.psz_host
,
1187 ( p_sys
->tlsmode
== EXPLICIT
) ? "ftpes-data"
1190 if( secure
== NULL
)
1192 msg_Err( p_access
, "cannot establish FTP/TLS session for data" \
1193 ": server not allowing new session ?" );
1194 return VLC_EGENERIC
;
1196 p_sys
->data
= secure
;
1202 static int ftp_StopStream ( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
1204 int ret
= VLC_SUCCESS
;
1206 if( ftp_SendCommand( p_access
, p_sys
, "ABOR" ) < 0 )
1208 msg_Warn( p_access
, "cannot abort file" );
1212 if( p_sys
->data
!= NULL
)
1214 vlc_tls_Close( p_sys
->data
);
1217 if( ret
== VLC_SUCCESS
)
1218 /* Read the final response from RETR/STOR, i.e. 426 or 226 */
1219 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
1222 if( ret
== VLC_SUCCESS
)
1223 /* Read the response from ABOR, i.e. 226 or 225 */
1224 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);