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 *****************************************************************************/
110 static ssize_t
Read( stream_t
*, void *, size_t );
111 static int Seek( stream_t
*, uint64_t );
112 static int Control( stream_t
*, int, va_list );
113 static int DirRead( stream_t
*, input_item_node_t
* );
115 static int OutSeek( sout_access_out_t
*, off_t
);
116 static ssize_t
Write( sout_access_out_t
*, block_t
* );
119 static int LoginUserPwd( vlc_object_t
*, access_sys_t
*,
120 const char *, const char *, bool * );
121 static void FeaturesCheck( void *, const char * );
123 typedef struct ftp_features_t
141 ftp_features_t features
;
142 vlc_tls_creds_t
*p_creds
;
143 enum tls_mode_e tlsmode
;
147 char sz_epsv_ip
[NI_MAXNUMERICHOST
];
152 #define GET_OUT_SYS( p_this ) \
153 ((access_sys_t *)(((sout_access_out_t *)(p_this))->p_sys))
155 static int ftp_SendCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
156 const char *fmt
, ... )
158 size_t fmtlen
= strlen( fmt
);
159 char fmtbuf
[fmtlen
+ 3];
161 memcpy( fmtbuf
, fmt
, fmtlen
);
162 memcpy( fmtbuf
+ fmtlen
, "\r\n", 3 );
168 va_start( args
, fmt
);
169 val
= vasprintf( &cmd
, fmtbuf
, args
);
171 if( unlikely(val
== -1) )
174 if( strncmp( cmd
, "PASS ", 5 ) && strncmp( cmd
, "ACCT ", 5 ) )
175 msg_Dbg( obj
, "sending request: \"%.*s\" (%d bytes)", val
-2, cmd
, val
);
177 msg_Dbg( obj
, "sending request: \"%.*s XXXX\" (XX bytes)", 4, cmd
);
179 if( vlc_tls_Write( sys
->cmd
, cmd
, val
) != val
)
181 msg_Err( obj
, "request failure" );
190 static char *ftp_GetLine( vlc_object_t
*obj
, access_sys_t
*sys
)
192 char *resp
= vlc_tls_GetLine( sys
->cmd
);
194 msg_Err( obj
, "response failure" );
198 /* TODO support this s**t :
199 RFC 959 allows the client to send certain TELNET strings at any moment,
200 even in the middle of a request:
203 * \377\376x where x is one byte.
204 * \377\375x where x is one byte. The server is obliged to send \377\374x
205 * immediately after reading x.
206 * \377\374x where x is one byte.
207 * \377\373x where x is one byte. The server is obliged to send \377\376x
208 * immediately after reading x.
209 * \377x for any other byte x.
211 These strings are not part of the requests, except in the case \377\377,
212 where the request contains one \377. */
213 static int ftp_RecvReply( vlc_object_t
*obj
, access_sys_t
*sys
,
214 char **restrict strp
,
215 void (*cb
)(void *, const char *), void *opaque
)
217 char *resp
= ftp_GetLine( obj
, sys
);
222 unsigned code
= strtoul( resp
, &end
, 10 );
223 if( (end
- resp
) != 3 || (*end
!= '-' && *end
!= ' ') )
225 msg_Err( obj
, "malformatted response" );
228 msg_Dbg( obj
, "received response: \"%s\"", resp
);
230 if( *end
== '-' ) /* Multi-line response */
237 char *line
= ftp_GetLine( obj
, sys
);
241 done
= !strncmp( resp
, line
, 4 );
259 static int ftp_RecvAnswer( vlc_object_t
*obj
, access_sys_t
*sys
,
260 int *restrict codep
, char **restrict strp
,
261 void (*cb
)(void *, const char *), void *opaque
)
264 int val
= ftp_RecvReply( obj
, sys
, &str
, cb
, opaque
);
265 if( (val
/ 100) == 1 )
266 { /* There can be zero or one preliminary reply per command */
268 val
= ftp_RecvReply( obj
, sys
, &str
, cb
, opaque
);
291 static void DummyLine( void *data
, const char *str
)
293 (void) data
; (void) str
;
296 static int ftp_RecvCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
297 int *restrict codep
, char **restrict strp
)
299 return ftp_RecvAnswer( obj
, sys
, codep
, strp
, DummyLine
, NULL
);
302 static int ftp_RecvCommandInit( vlc_object_t
*obj
, access_sys_t
*sys
)
304 int val
= ftp_RecvReply( obj
, sys
, NULL
, DummyLine
, NULL
);
310 static int ftp_StartStream( vlc_object_t
*, access_sys_t
*, uint64_t, bool );
311 static int ftp_StopStream ( vlc_object_t
*, access_sys_t
* );
313 static int readTLSMode( vlc_object_t
*obj
, access_sys_t
*p_sys
,
314 const char * psz_access
)
316 if ( !strncmp( psz_access
, "ftps", 4 ) )
317 p_sys
->tlsmode
= IMPLICIT
;
319 if ( !strncmp( psz_access
, "ftpes", 5 ) )
320 p_sys
->tlsmode
= EXPLICIT
;
323 p_sys
->p_creds
= NULL
;
324 p_sys
->tlsmode
= NONE
;
328 p_sys
->p_creds
= vlc_tls_ClientCreate( obj
);
329 return (p_sys
->p_creds
!= NULL
) ? 0 : -1;
332 static int createCmdTLS( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
333 const char *psz_session_name
)
335 /* TLS/SSL handshake */
336 vlc_tls_t
*secure
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
,
343 msg_Err( p_access
, "cannot establish FTP/TLS session on command channel" );
350 static void clearCmd( access_sys_t
*p_sys
)
352 if( p_sys
->cmd
!= NULL
)
354 vlc_tls_Close( p_sys
->cmd
);
359 static int Login( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
363 /* *** Open a TCP connection with server *** */
364 p_sys
->cmd
= vlc_tls_SocketOpenTCP( p_access
, p_sys
->url
.psz_host
,
366 if( p_sys
->cmd
== NULL
)
368 msg_Err( p_access
, "connection failed" );
369 vlc_dialog_display_error( p_access
, _("Network interaction failed"), "%s",
370 _("VLC could not connect with the given server.") );
374 if ( p_sys
->tlsmode
== IMPLICIT
) /* FTPS Mode */
376 if ( createCmdTLS( p_access
, p_sys
, "ftps") < 0 )
380 while( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) == 1 );
382 if( i_answer
/ 100 != 2 )
384 msg_Err( p_access
, "connection rejected" );
385 vlc_dialog_display_error( p_access
, _("Network interaction failed"), "%s",
386 _("VLC's connection to the given server was rejected.") );
390 msg_Dbg( p_access
, "connection accepted (%d)", i_answer
);
392 /* Features check first */
393 if( ftp_SendCommand( p_access
, p_sys
, "FEAT" ) < 0
394 || ftp_RecvAnswer( p_access
, p_sys
, NULL
, NULL
,
395 FeaturesCheck
, &p_sys
->features
) < 0 )
397 msg_Err( p_access
, "cannot get server features" );
401 /* Create TLS Session */
402 if( p_sys
->tlsmode
== EXPLICIT
)
404 if ( ! p_sys
->features
.b_authtls
)
406 msg_Err( p_access
, "Server does not support TLS" );
410 if( ftp_SendCommand( p_access
, p_sys
, "AUTH TLS" ) < 0
411 || ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0
414 msg_Err( p_access
, "cannot switch to TLS: server replied with code %d",
419 if( createCmdTLS( p_access
, p_sys
, "ftpes") < 0 )
425 if( p_sys
->tlsmode
!= NONE
)
427 if( ftp_SendCommand( p_access
, p_sys
, "PBSZ 0" ) < 0 ||
428 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
431 msg_Err( p_access
, "Can't truncate Protection buffer size for TLS" );
435 if( ftp_SendCommand( p_access
, p_sys
, "PROT P" ) < 0 ||
436 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
439 msg_Err( p_access
, "Can't set Data channel protection" );
445 vlc_credential credential
;
446 vlc_UrlParse( &url
, ((stream_t
*)p_access
)->psz_url
);
447 vlc_credential_init( &credential
, &url
);
448 bool b_logged
= false;
450 /* First: try credentials from url / option */
451 vlc_credential_get( &credential
, p_access
, "ftp-user", "ftp-pwd",
455 if( credential
.psz_username
!= NULL
)
457 if( LoginUserPwd( p_access
, p_sys
, credential
.psz_username
,
458 credential
.psz_password
, &b_logged
) != 0
464 /* No crendential specified: show the dialog with a "anonymous"
466 credential
.psz_username
= "anonymous";
469 while( vlc_credential_get( &credential
, p_access
, "ftp-user", "ftp-pwd",
470 LOGIN_DIALOG_TITLE
, LOGIN_DIALOG_TEXT
,
475 vlc_credential_store( &credential
, p_access
);
476 vlc_credential_clean( &credential
);
477 vlc_UrlClean( &url
);
480 vlc_credential_clean( &credential
);
481 vlc_UrlClean( &url
);
487 static int LoginUserPwd( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
488 const char *psz_user
, const char *psz_pwd
,
493 /* Send credentials over channel */
494 if( ftp_SendCommand( p_access
, p_sys
, "USER %s", psz_user
) < 0 ||
495 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
498 switch( i_answer
/ 100 )
501 /* X.509 auth successful after AUTH TLS / RFC 2228 sec. 4 */
502 if ( i_answer
== 232 )
503 msg_Dbg( p_access
, "user accepted and authenticated" );
505 msg_Dbg( p_access
, "user accepted" );
508 msg_Dbg( p_access
, "password needed" );
510 if( ftp_SendCommand( p_access
, p_sys
, "PASS %s", psz_pwd
) < 0 ||
511 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
514 switch( i_answer
/ 100 )
517 msg_Dbg( p_access
, "password accepted" );
522 msg_Dbg( p_access
, "account needed" );
523 psz
= var_InheritString( p_access
, "ftp-account" );
524 if( ftp_SendCommand( p_access
, p_sys
, "ACCT %s",
526 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
533 if( i_answer
/ 100 != 2 )
535 msg_Err( p_access
, "account rejected" );
536 vlc_dialog_display_error( p_access
,
537 _("Network interaction failed"),
538 "%s", _("Your account was rejected.") );
541 msg_Dbg( p_access
, "account accepted" );
546 msg_Warn( p_access
, "password rejected" );
552 msg_Warn( p_access
, "user rejected" );
561 static void FeaturesCheck( void *opaque
, const char *feature
)
563 ftp_features_t
*features
= opaque
;
565 if( strcasestr( feature
, "UTF8" ) != NULL
)
566 features
->b_unicode
= true;
568 if( strcasestr( feature
, "AUTH TLS" ) != NULL
)
569 features
->b_authtls
= true;
571 if( strcasestr( feature
, "MLST" ) != NULL
)
572 features
->b_mlst
= true;
575 static const char *IsASCII( const char *str
)
578 for( const char *p
= str
; (c
= *p
) != '\0'; p
++ )
584 static int Connect( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
586 if( Login( p_access
, p_sys
) < 0 )
589 /* Extended passive mode */
590 if( ftp_SendCommand( p_access
, p_sys
, "EPSV ALL" ) < 0 )
592 msg_Err( p_access
, "cannot request extended passive mode" );
596 if( ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) == 2 )
598 int fd
= vlc_tls_GetFD(p_sys
->cmd
);
599 if( net_GetPeerAddress( fd
, p_sys
->sz_epsv_ip
, NULL
) )
604 /* If ESPV ALL fails, we fallback to PASV.
605 * We have to restart the connection in case there is a NAT that
606 * understands EPSV ALL in the way, and hence won't allow PASV on
607 * the initial connection.
609 msg_Info( p_access
, "FTP Extended passive mode disabled" );
612 if( Login( p_access
, p_sys
) )
616 if( p_sys
->url
.psz_path
&&
617 (p_sys
->features
.b_unicode
? IsUTF8
: IsASCII
)(p_sys
->url
.psz_path
) == NULL
)
619 msg_Err( p_access
, "unsupported path: \"%s\"", p_sys
->url
.psz_path
);
623 /* check binary mode support */
624 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
625 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) != 2 )
627 msg_Err( p_access
, "cannot set binary transfer mode" );
639 static int parseURL( vlc_url_t
*url
, const char *path
, enum tls_mode_e mode
)
644 /* *** Parse URL and get server addr/port and path *** */
645 while( *path
== '/' )
648 vlc_UrlParse( url
, path
);
650 if( url
->psz_host
== NULL
|| *url
->psz_host
== '\0' )
653 if( url
->i_port
<= 0 )
655 if( mode
== IMPLICIT
)
656 url
->i_port
= IPPORT_FTPS
;
658 url
->i_port
= IPPORT_FTP
; /* default port */
661 if( url
->psz_path
== NULL
)
663 /* FTP URLs are relative to user's default directory (RFC1738 §3.2)
664 For absolute path use ftp://foo.bar//usr/local/etc/filename */
665 /* FIXME: we should issue a series of CWD, one per slash */
668 assert( url
->psz_path
[0] == '/' );
672 char *type
= strstr( url
->psz_path
, ";type=" );
676 if( strchr( "iI", type
[6] ) == NULL
)
677 return VLC_EGENERIC
; /* ASCII and directory not supported */
679 vlc_uri_decode( url
->psz_path
);
684 /****************************************************************************
685 * Open: connect to ftp server and ask for file
686 ****************************************************************************/
687 static int InOpen( vlc_object_t
*p_this
)
689 stream_t
*p_access
= (stream_t
*)p_this
;
695 p_sys
= p_access
->p_sys
= (access_sys_t
*)vlc_calloc( p_this
, 1, sizeof( access_sys_t
) );
701 p_sys
->size
= UINT64_MAX
;
703 if( readTLSMode( p_this
, p_sys
, p_access
->psz_name
) )
706 if( parseURL( &p_sys
->url
, p_access
->psz_url
, p_sys
->tlsmode
) )
709 if( Connect( p_this
, p_sys
) )
714 if( p_sys
->url
.psz_path
== NULL
|| !*p_sys
->url
.psz_path
)
720 if( ftp_SendCommand( p_this
, p_sys
, "SIZE %s",
721 p_sys
->url
.psz_path
) < 0 )
724 int val
= ftp_RecvCommand( p_this
, p_sys
, NULL
, &psz_arg
);
728 p_sys
->size
= atoll( &psz_arg
[4] );
730 msg_Dbg( p_access
, "file size: %"PRIu64
, p_sys
->size
);
736 if( ftp_SendCommand( p_this
, p_sys
, "CWD %s",
737 p_sys
->url
.psz_path
) < 0 )
740 if( ftp_RecvCommand( p_this
, p_sys
, NULL
, NULL
) == 2 )
746 msg_Err( p_this
, "file or directory does not exist" );
752 p_access
->pf_readdir
= DirRead
;
753 p_access
->pf_control
= access_vaDirectoryControlHelper
;
755 ACCESS_SET_CALLBACKS( Read
, NULL
, Control
, Seek
); \
757 /* Start the 'stream' */
758 if( ftp_StartStream( p_this
, p_sys
, 0, b_directory
) < 0 )
760 msg_Err( p_this
, "cannot retrieve file" );
770 vlc_UrlClean( &p_sys
->url
);
771 vlc_tls_Delete( p_sys
->p_creds
);
776 static int OutOpen( vlc_object_t
*p_this
)
778 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
781 p_sys
= vlc_calloc( p_this
, 1, sizeof( *p_sys
) );
789 if( readTLSMode( p_this
, p_sys
, p_access
->psz_access
) )
792 if( parseURL( &p_sys
->url
, p_access
->psz_path
, p_sys
->tlsmode
) )
794 if( p_sys
->url
.psz_path
== NULL
)
796 msg_Err( p_this
, "no filename specified" );
800 if( Connect( p_this
, p_sys
) )
803 /* Start the 'stream' */
804 if( ftp_StartStream( p_this
, p_sys
, 0, false ) < 0 )
806 msg_Err( p_access
, "cannot store file" );
811 p_access
->pf_seek
= OutSeek
;
812 p_access
->pf_write
= Write
;
813 p_access
->p_sys
= (void *)p_sys
;
818 vlc_UrlClean( &p_sys
->url
);
819 vlc_tls_Delete( p_sys
->p_creds
);
824 /*****************************************************************************
825 * Close: free unused data structures
826 *****************************************************************************/
827 static void Close( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
829 msg_Dbg( p_access
, "stopping stream" );
830 ftp_StopStream( p_access
, p_sys
);
832 if( ftp_SendCommand( p_access
, p_sys
, "QUIT" ) < 0 )
834 msg_Warn( p_access
, "cannot quit" );
838 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
844 vlc_UrlClean( &p_sys
->url
);
845 vlc_tls_Delete( p_sys
->p_creds
);
848 static void InClose( vlc_object_t
*p_this
)
850 Close( p_this
, ((stream_t
*)p_this
)->p_sys
);
854 static void OutClose( vlc_object_t
*p_this
)
856 Close( p_this
, GET_OUT_SYS(p_this
));
861 /*****************************************************************************
862 * Seek: try to go at the right place
863 *****************************************************************************/
864 static int SeekCommon( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
867 msg_Dbg( p_access
, "seeking to %"PRIu64
, i_pos
);
869 ftp_StopStream( p_access
, p_sys
);
871 if( ftp_StartStream( p_access
, p_sys
, i_pos
, false ) < 0 )
876 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
878 access_sys_t
*p_sys
= p_access
->p_sys
;
880 int val
= SeekCommon( (vlc_object_t
*)p_access
, p_sys
, i_pos
);
884 p_sys
->offset
= i_pos
;
890 static int OutSeek( sout_access_out_t
*p_access
, off_t i_pos
)
892 return SeekCommon((vlc_object_t
*)p_access
, GET_OUT_SYS(p_access
), i_pos
);
896 /*****************************************************************************
898 *****************************************************************************/
899 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
901 access_sys_t
*p_sys
= p_access
->p_sys
;
903 assert( p_sys
->data
!= NULL
);
904 assert( !p_sys
->out
);
906 ssize_t i_read
= vlc_tls_Read( p_sys
->data
, p_buffer
, i_len
, false );
908 p_sys
->offset
+= i_read
;
909 else if( errno
!= EINTR
&& errno
!= EAGAIN
)
911 msg_Err( p_access
, "receive error: %s", vlc_strerror_c(errno
) );
918 /*****************************************************************************
920 *****************************************************************************/
921 static int DirRead (stream_t
*p_access
, input_item_node_t
*p_current_node
)
923 access_sys_t
*p_sys
= p_access
->p_sys
;
924 int i_ret
= VLC_SUCCESS
;
926 assert( p_sys
->data
!= NULL
);
927 assert( !p_sys
->out
);
929 struct vlc_readdir_helper rdh
;
930 vlc_readdir_helper_init( &rdh
, p_access
, p_current_node
);
932 while (i_ret
== VLC_SUCCESS
)
935 int type
= ITEM_TYPE_UNKNOWN
;
937 char *psz_line
= vlc_tls_GetLine( p_sys
->data
);
938 if( psz_line
== NULL
)
941 if( p_sys
->features
.b_mlst
)
943 /* MLST Format is key=val;key=val...; FILENAME */
944 if( strstr( psz_line
, "type=dir" ) )
945 type
= ITEM_TYPE_DIRECTORY
;
946 if( strstr( psz_line
, "type=file" ) )
947 type
= ITEM_TYPE_FILE
;
949 /* Get the filename or fail */
950 psz_file
= strchr( psz_line
, ' ' );
955 msg_Warn( p_access
, "Empty filename in MLST list" );
964 char *psz_filename
= vlc_uri_encode( psz_file
);
965 if( psz_filename
!= NULL
&&
966 asprintf( &psz_uri
, "%s://%s:%d%s%s/%s",
967 ( p_sys
->tlsmode
== NONE
) ? "ftp" :
968 ( ( p_sys
->tlsmode
== IMPLICIT
) ? "ftps" : "ftpes" ),
969 p_sys
->url
.psz_host
, p_sys
->url
.i_port
,
970 p_sys
->url
.psz_path
? "/" : "",
971 p_sys
->url
.psz_path
? p_sys
->url
.psz_path
: "",
972 psz_filename
) != -1 )
974 i_ret
= vlc_readdir_helper_additem( &rdh
, psz_uri
, NULL
, psz_file
,
978 free( psz_filename
);
982 vlc_readdir_helper_finish( &rdh
, i_ret
== VLC_SUCCESS
);
986 /*****************************************************************************
988 *****************************************************************************/
990 static ssize_t
Write( sout_access_out_t
*p_access
, block_t
*p_buffer
)
992 access_sys_t
*p_sys
= GET_OUT_SYS(p_access
);
995 assert( p_sys
->data
!= NULL
);
997 while( p_buffer
!= NULL
)
999 block_t
*p_next
= p_buffer
->p_next
;
1001 i_write
+= vlc_tls_Write( p_sys
->data
,
1002 p_buffer
->p_buffer
, p_buffer
->i_buffer
);
1003 block_Release( p_buffer
);
1012 /*****************************************************************************
1014 *****************************************************************************/
1015 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
1017 access_sys_t
*sys
= p_access
->p_sys
;
1023 case STREAM_CAN_SEEK
:
1024 pb_bool
= va_arg( args
, bool * );
1027 case STREAM_CAN_FASTSEEK
:
1028 pb_bool
= va_arg( args
, bool * );
1031 case STREAM_CAN_PAUSE
:
1032 pb_bool
= va_arg( args
, bool * );
1033 *pb_bool
= true; /* FIXME */
1035 case STREAM_CAN_CONTROL_PACE
:
1036 pb_bool
= va_arg( args
, bool * );
1037 *pb_bool
= true; /* FIXME */
1039 case STREAM_GET_SIZE
:
1040 if( sys
->size
== UINT64_MAX
)
1041 return VLC_EGENERIC
;
1042 *va_arg( args
, uint64_t * ) = sys
->size
;
1045 case STREAM_GET_PTS_DELAY
:
1046 pi_64
= va_arg( args
, int64_t * );
1047 *pi_64
= INT64_C(1000)
1048 * var_InheritInteger( p_access
, "network-caching" );
1051 case STREAM_SET_PAUSE_STATE
:
1052 pb_bool
= va_arg( args
, bool * );
1054 return Seek( p_access
, sys
->offset
);
1058 return VLC_EGENERIC
;
1064 static int ftp_StartStream( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
1065 uint64_t i_start
, bool b_directory
)
1067 char psz_ipv4
[16], *psz_ip
= p_sys
->sz_epsv_ip
;
1069 char *psz_arg
, *psz_parser
;
1072 assert( p_sys
->data
== NULL
);
1074 if( ( ftp_SendCommand( p_access
, p_sys
, *psz_ip
? "EPSV" : "PASV" ) < 0 )
1075 || ( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, &psz_arg
) != 2 ) )
1077 msg_Err( p_access
, "cannot set passive mode" );
1078 return VLC_EGENERIC
;
1081 psz_parser
= strchr( psz_arg
, '(' );
1082 if( psz_parser
== NULL
)
1085 msg_Err( p_access
, "cannot parse passive mode response" );
1086 return VLC_EGENERIC
;
1091 if( sscanf( psz_parser
, "(%*3c%u", &i_port
) < 1 )
1094 msg_Err( p_access
, "cannot parse passive mode response" );
1095 return VLC_EGENERIC
;
1100 unsigned a1
, a2
, a3
, a4
, p1
, p2
;
1102 if( ( sscanf( psz_parser
, "(%u,%u,%u,%u,%u,%u", &a1
, &a2
, &a3
, &a4
,
1103 &p1
, &p2
) < 6 ) || ( a1
> 255 ) || ( a2
> 255 )
1104 || ( a3
> 255 ) || ( a4
> 255 ) || ( p1
> 255 ) || ( p2
> 255 ) )
1107 msg_Err( p_access
, "cannot parse passive mode response" );
1108 return VLC_EGENERIC
;
1111 sprintf( psz_ipv4
, "%u.%u.%u.%u", a1
, a2
, a3
, a4
);
1113 i_port
= (p1
<< 8) | p2
;
1117 msg_Dbg( p_access
, "ip:%s port:%d", psz_ip
, i_port
);
1119 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
1120 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) != 2 )
1122 msg_Err( p_access
, "cannot set binary transfer mode" );
1123 return VLC_EGENERIC
;
1128 if( ftp_SendCommand( p_access
, p_sys
, "REST %"PRIu64
, i_start
) < 0 ||
1129 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) > 3 )
1131 msg_Err( p_access
, "cannot set restart offset" );
1132 return VLC_EGENERIC
;
1136 msg_Dbg( p_access
, "waiting for data connection..." );
1137 p_sys
->data
= vlc_tls_SocketOpenTCP( p_access
, psz_ip
, i_port
);
1138 if( p_sys
->data
== NULL
)
1140 msg_Err( p_access
, "failed to connect with server" );
1141 return VLC_EGENERIC
;
1143 msg_Dbg( p_access
, "connection with \"%s:%d\" successful",
1148 if( p_sys
->features
.b_mlst
&&
1149 ftp_SendCommand( p_access
, p_sys
, "MLSD" ) >= 0 &&
1150 ftp_RecvCommandInit( p_access
, p_sys
) == 1 )
1152 msg_Dbg( p_access
, "Using MLST extension to list" );
1155 if( ftp_SendCommand( p_access
, p_sys
, "NLST" ) < 0 ||
1156 ftp_RecvCommandInit( p_access
, p_sys
) == 1 )
1158 msg_Err( p_access
, "cannot list directory contents" );
1159 return VLC_EGENERIC
;
1165 assert( p_sys
->url
.psz_path
);
1166 if( ftp_SendCommand( p_access
, p_sys
, "%s %s",
1167 p_sys
->out
? "STOR" : "RETR",
1168 p_sys
->url
.psz_path
) < 0
1169 || ftp_RecvCommandInit( p_access
, p_sys
) != 1 )
1171 msg_Err( p_access
, "cannot retrieve file" );
1172 return VLC_EGENERIC
;
1176 if( p_sys
->tlsmode
!= NONE
)
1178 /* FIXME: Do Reuse TLS Session */
1179 /* TLS/SSL handshake */
1180 vlc_tls_t
*secure
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
,
1181 p_sys
->data
, p_sys
->url
.psz_host
,
1182 ( p_sys
->tlsmode
== EXPLICIT
) ? "ftpes-data"
1185 if( secure
== NULL
)
1187 msg_Err( p_access
, "cannot establish FTP/TLS session for data" \
1188 ": server not allowing new session ?" );
1189 return VLC_EGENERIC
;
1191 p_sys
->data
= secure
;
1197 static int ftp_StopStream ( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
1199 int ret
= VLC_SUCCESS
;
1201 if( ftp_SendCommand( p_access
, p_sys
, "ABOR" ) < 0 )
1203 msg_Warn( p_access
, "cannot abort file" );
1207 if( p_sys
->data
!= NULL
)
1209 vlc_tls_Close( p_sys
->data
);
1212 if( ret
== VLC_SUCCESS
)
1213 /* Read the final response from RETR/STOR, i.e. 426 or 226 */
1214 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
1217 if( ret
== VLC_SUCCESS
)
1218 /* Read the response from ABOR, i.e. 226 or 225 */
1219 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);