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", "anonymous", USER_TEXT
, USER_LONGTEXT
,
90 add_string( "ftp-pwd", "anonymous@example.com", PASS_TEXT
,
91 PASS_LONGTEXT
, false )
92 add_string( "ftp-account", "anonymous", ACCOUNT_TEXT
,
93 ACCOUNT_LONGTEXT
, false )
94 add_shortcut( "ftp", "ftps", "ftpes" )
95 set_callbacks( InOpen
, InClose
)
99 set_shortname( "FTP" )
100 set_description( N_("FTP upload output") )
101 set_capability( "sout access", 0 )
102 set_category( CAT_SOUT
)
103 set_subcategory( SUBCAT_SOUT_ACO
)
104 add_shortcut( "ftp", "ftps", "ftpes" )
105 set_callbacks( OutOpen
, OutClose
)
109 /*****************************************************************************
111 *****************************************************************************/
112 static ssize_t
Read( access_t
*, void *, size_t );
113 static int Seek( access_t
*, uint64_t );
114 static int Control( access_t
*, int, va_list );
115 static int DirRead( access_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_creds_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))
157 static int ftp_SendCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
158 const char *fmt
, ... )
160 size_t fmtlen
= strlen( fmt
);
161 char fmtbuf
[fmtlen
+ 3];
163 memcpy( fmtbuf
, fmt
, fmtlen
);
164 memcpy( fmtbuf
+ fmtlen
, "\r\n", 3 );
170 va_start( args
, fmt
);
171 val
= vasprintf( &cmd
, fmtbuf
, args
);
173 if( unlikely(val
== -1) )
176 if( strncmp( cmd
, "PASS ", 5 ) && strncmp( cmd
, "ACCT ", 5 ) )
177 msg_Dbg( obj
, "sending request: \"%.*s\" (%d bytes)", val
-2, cmd
, val
);
179 msg_Dbg( obj
, "sending request: \"%.*s XXXX\" (XX bytes)", 4, cmd
);
181 if( vlc_tls_Write( sys
->cmd
, cmd
, val
) != val
)
183 msg_Err( obj
, "request failure" );
192 static char *ftp_GetLine( vlc_object_t
*obj
, access_sys_t
*sys
)
194 char *resp
= vlc_tls_GetLine( sys
->cmd
);
196 msg_Err( obj
, "response failure" );
200 /* TODO support this s**t :
201 RFC 959 allows the client to send certain TELNET strings at any moment,
202 even in the middle of a request:
205 * \377\376x where x is one byte.
206 * \377\375x where x is one byte. The server is obliged to send \377\374x
207 * immediately after reading x.
208 * \377\374x where x is one byte.
209 * \377\373x where x is one byte. The server is obliged to send \377\376x
210 * immediately after reading x.
211 * \377x for any other byte x.
213 These strings are not part of the requests, except in the case \377\377,
214 where the request contains one \377. */
215 static int ftp_RecvReply( vlc_object_t
*obj
, access_sys_t
*sys
,
216 char **restrict strp
,
217 void (*cb
)(void *, const char *), void *opaque
)
219 char *resp
= ftp_GetLine( obj
, sys
);
224 unsigned code
= strtoul( resp
, &end
, 10 );
225 if( (end
- resp
) != 3 || (*end
!= '-' && *end
!= ' ') )
227 msg_Err( obj
, "malformatted response" );
230 msg_Dbg( obj
, "received response: \"%s\"", resp
);
232 if( *end
== '-' ) /* Multi-line response */
239 char *line
= ftp_GetLine( obj
, sys
);
243 done
= !strncmp( resp
, line
, 4 );
261 static int ftp_RecvAnswer( vlc_object_t
*obj
, access_sys_t
*sys
,
262 int *restrict codep
, char **restrict strp
,
263 void (*cb
)(void *, const char *), void *opaque
)
266 int val
= ftp_RecvReply( obj
, sys
, &str
, cb
, opaque
);
267 if( (val
/ 100) == 1 )
268 { /* There can be zero or one preliminary reply per command */
270 val
= ftp_RecvReply( obj
, sys
, &str
, cb
, opaque
);
293 static void DummyLine( void *data
, const char *str
)
295 (void) data
; (void) str
;
298 static int ftp_RecvCommand( vlc_object_t
*obj
, access_sys_t
*sys
,
299 int *restrict codep
, char **restrict strp
)
301 return ftp_RecvAnswer( obj
, sys
, codep
, strp
, DummyLine
, NULL
);
304 static int ftp_StartStream( vlc_object_t
*, access_sys_t
*, uint64_t, bool );
305 static int ftp_StopStream ( vlc_object_t
*, access_sys_t
* );
307 static int readTLSMode( vlc_object_t
*obj
, access_sys_t
*p_sys
,
308 const char * psz_access
)
310 if ( !strncmp( psz_access
, "ftps", 4 ) )
311 p_sys
->tlsmode
= IMPLICIT
;
313 if ( !strncmp( psz_access
, "ftpes", 5 ) )
314 p_sys
->tlsmode
= EXPLICIT
;
317 p_sys
->p_creds
= NULL
;
318 p_sys
->tlsmode
= NONE
;
322 p_sys
->p_creds
= vlc_tls_ClientCreate( obj
);
323 return (p_sys
->p_creds
!= NULL
) ? 0 : -1;
326 static int createCmdTLS( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
327 const char *psz_session_name
)
329 /* TLS/SSL handshake */
330 vlc_tls_t
*secure
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
,
337 msg_Err( p_access
, "cannot establish FTP/TLS session on command channel" );
344 static void clearCmd( access_sys_t
*p_sys
)
346 if( p_sys
->cmd
!= NULL
)
348 vlc_tls_Close( p_sys
->cmd
);
353 static int Login( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
357 /* *** Open a TCP connection with server *** */
358 p_sys
->cmd
= vlc_tls_SocketOpenTCP( p_access
, p_sys
->url
.psz_host
,
360 if( p_sys
->cmd
== NULL
)
362 msg_Err( p_access
, "connection failed" );
363 vlc_dialog_display_error( p_access
, _("Network interaction failed"), "%s",
364 _("VLC could not connect with the given server.") );
368 if ( p_sys
->tlsmode
== IMPLICIT
) /* FTPS Mode */
370 if ( createCmdTLS( p_access
, p_sys
, "ftps") < 0 )
374 while( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) == 1 );
376 if( i_answer
/ 100 != 2 )
378 msg_Err( p_access
, "connection rejected" );
379 vlc_dialog_display_error( p_access
, _("Network interaction failed"), "%s",
380 _("VLC's connection to the given server was rejected.") );
384 msg_Dbg( p_access
, "connection accepted (%d)", i_answer
);
386 /* Features check first */
387 if( ftp_SendCommand( p_access
, p_sys
, "FEAT" ) < 0
388 || ftp_RecvAnswer( p_access
, p_sys
, NULL
, NULL
,
389 FeaturesCheck
, &p_sys
->features
) < 0 )
391 msg_Err( p_access
, "cannot get server features" );
395 /* Create TLS Session */
396 if( p_sys
->tlsmode
== EXPLICIT
)
398 if ( ! p_sys
->features
.b_authtls
)
400 msg_Err( p_access
, "Server does not support TLS" );
404 if( ftp_SendCommand( p_access
, p_sys
, "AUTH TLS" ) < 0
405 || ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0
408 msg_Err( p_access
, "cannot switch to TLS: server replied with code %d",
413 if( createCmdTLS( p_access
, p_sys
, "ftpes") < 0 )
419 if( p_sys
->tlsmode
!= NONE
)
421 if( ftp_SendCommand( p_access
, p_sys
, "PBSZ 0" ) < 0 ||
422 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
425 msg_Err( p_access
, "Can't truncate Protection buffer size for TLS" );
429 if( ftp_SendCommand( p_access
, p_sys
, "PROT P" ) < 0 ||
430 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 ||
433 msg_Err( p_access
, "Can't set Data channel protection" );
439 vlc_credential credential
;
440 vlc_UrlParse( &url
, ((access_t
*)p_access
)->psz_url
);
441 vlc_credential_init( &credential
, &url
);
442 bool b_logged
= false;
444 while( vlc_credential_get( &credential
, p_access
, "ftp-user", "ftp-pwd",
445 LOGIN_DIALOG_TITLE
, LOGIN_DIALOG_TEXT
,
447 && LoginUserPwd( p_access
, p_sys
, credential
.psz_username
,
448 credential
.psz_password
, &b_logged
) == 0
452 vlc_credential_store( &credential
, p_access
);
453 vlc_credential_clean( &credential
);
454 vlc_UrlClean( &url
);
457 vlc_credential_clean( &credential
);
458 vlc_UrlClean( &url
);
464 static int LoginUserPwd( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
465 const char *psz_user
, const char *psz_pwd
,
470 /* Send credentials over channel */
471 if( ftp_SendCommand( p_access
, p_sys
, "USER %s", psz_user
) < 0 ||
472 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
475 switch( i_answer
/ 100 )
478 /* X.509 auth successful after AUTH TLS / RFC 2228 sec. 4 */
479 if ( i_answer
== 232 )
480 msg_Dbg( p_access
, "user accepted and authenticated" );
482 msg_Dbg( p_access
, "user accepted" );
485 msg_Dbg( p_access
, "password needed" );
487 if( ftp_SendCommand( p_access
, p_sys
, "PASS %s", psz_pwd
) < 0 ||
488 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
491 switch( i_answer
/ 100 )
494 msg_Dbg( p_access
, "password accepted" );
499 msg_Dbg( p_access
, "account needed" );
500 psz
= var_InheritString( p_access
, "ftp-account" );
501 if( ftp_SendCommand( p_access
, p_sys
, "ACCT %s",
503 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) < 0 )
510 if( i_answer
/ 100 != 2 )
512 msg_Err( p_access
, "account rejected" );
513 vlc_dialog_display_error( p_access
,
514 _("Network interaction failed"),
515 "%s", _("Your account was rejected.") );
518 msg_Dbg( p_access
, "account accepted" );
523 msg_Warn( p_access
, "password rejected" );
529 msg_Warn( p_access
, "user rejected" );
538 static void FeaturesCheck( void *opaque
, const char *feature
)
540 ftp_features_t
*features
= opaque
;
542 if( strcasestr( feature
, "UTF8" ) != NULL
)
543 features
->b_unicode
= true;
545 if( strcasestr( feature
, "AUTH TLS" ) != NULL
)
546 features
->b_authtls
= true;
548 if( strcasestr( feature
, "MLST" ) != NULL
)
549 features
->b_mlst
= true;
552 static const char *IsASCII( const char *str
)
555 for( const char *p
= str
; (c
= *p
) != '\0'; p
++ )
561 static int Connect( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
563 if( Login( p_access
, p_sys
) < 0 )
566 /* Extended passive mode */
567 if( ftp_SendCommand( p_access
, p_sys
, "EPSV ALL" ) < 0 )
569 msg_Err( p_access
, "cannot request extended passive mode" );
573 if( ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) == 2 )
575 int fd
= vlc_tls_GetFD(p_sys
->cmd
);
576 if( net_GetPeerAddress( fd
, p_sys
->sz_epsv_ip
, NULL
) )
581 /* If ESPV ALL fails, we fallback to PASV.
582 * We have to restart the connection in case there is a NAT that
583 * understands EPSV ALL in the way, and hence won't allow PASV on
584 * the initial connection.
586 msg_Info( p_access
, "FTP Extended passive mode disabled" );
589 if( Login( p_access
, p_sys
) )
593 if( p_sys
->url
.psz_path
&&
594 (p_sys
->features
.b_unicode
? IsUTF8
: IsASCII
)(p_sys
->url
.psz_path
) == NULL
)
596 msg_Err( p_access
, "unsupported path: \"%s\"", p_sys
->url
.psz_path
);
600 /* check binary mode support */
601 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
602 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) != 2 )
604 msg_Err( p_access
, "cannot set binary transfer mode" );
616 static int parseURL( vlc_url_t
*url
, const char *path
, enum tls_mode_e mode
)
621 /* *** Parse URL and get server addr/port and path *** */
622 while( *path
== '/' )
625 vlc_UrlParse( url
, path
);
627 if( url
->psz_host
== NULL
|| *url
->psz_host
== '\0' )
630 if( url
->i_port
<= 0 )
632 if( mode
== IMPLICIT
)
633 url
->i_port
= IPPORT_FTPS
;
635 url
->i_port
= IPPORT_FTP
; /* default port */
638 if( url
->psz_path
== NULL
)
640 /* FTP URLs are relative to user's default directory (RFC1738 §3.2)
641 For absolute path use ftp://foo.bar//usr/local/etc/filename */
642 /* FIXME: we should issue a series of CWD, one per slash */
645 assert( url
->psz_path
[0] == '/' );
649 char *type
= strstr( url
->psz_path
, ";type=" );
653 if( strchr( "iI", type
[6] ) == NULL
)
654 return VLC_EGENERIC
; /* ASCII and directory not supported */
656 vlc_uri_decode( url
->psz_path
);
661 /****************************************************************************
662 * Open: connect to ftp server and ask for file
663 ****************************************************************************/
664 static int InOpen( vlc_object_t
*p_this
)
666 access_t
*p_access
= (access_t
*)p_this
;
672 p_sys
= p_access
->p_sys
= (access_sys_t
*)calloc( 1, sizeof( access_sys_t
) );
678 p_sys
->size
= UINT64_MAX
;
680 if( readTLSMode( p_this
, p_sys
, p_access
->psz_name
) )
683 if( parseURL( &p_sys
->url
, p_access
->psz_url
, p_sys
->tlsmode
) )
686 if( Connect( p_this
, p_sys
) )
691 if( p_sys
->url
.psz_path
== NULL
|| !*p_sys
->url
.psz_path
)
697 if( ftp_SendCommand( p_this
, p_sys
, "SIZE %s",
698 p_sys
->url
.psz_path
) < 0 )
701 int val
= ftp_RecvCommand( p_this
, p_sys
, NULL
, &psz_arg
);
705 p_sys
->size
= atoll( &psz_arg
[4] );
707 msg_Dbg( p_access
, "file size: %"PRIu64
, p_sys
->size
);
713 if( ftp_SendCommand( p_this
, p_sys
, "CWD %s",
714 p_sys
->url
.psz_path
) < 0 )
717 if( ftp_RecvCommand( p_this
, p_sys
, NULL
, NULL
) == 2 )
723 msg_Err( p_this
, "file or directory does not exist" );
729 p_access
->pf_readdir
= DirRead
;
730 p_access
->pf_control
= access_vaDirectoryControlHelper
;
732 ACCESS_SET_CALLBACKS( Read
, NULL
, Control
, Seek
); \
734 /* Start the 'stream' */
735 if( ftp_StartStream( p_this
, p_sys
, 0, b_directory
) < 0 )
737 msg_Err( p_this
, "cannot retrieve file" );
747 vlc_UrlClean( &p_sys
->url
);
748 vlc_tls_Delete( p_sys
->p_creds
);
754 static int OutOpen( vlc_object_t
*p_this
)
756 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
759 p_sys
= calloc( 1, sizeof( *p_sys
) );
767 if( readTLSMode( p_this
, p_sys
, p_access
->psz_access
) )
770 if( parseURL( &p_sys
->url
, p_access
->psz_path
, p_sys
->tlsmode
) )
772 if( p_sys
->url
.psz_path
== NULL
)
774 msg_Err( p_this
, "no filename specified" );
778 if( Connect( p_this
, p_sys
) )
781 /* Start the 'stream' */
782 if( ftp_StartStream( p_this
, p_sys
, 0, false ) < 0 )
784 msg_Err( p_access
, "cannot store file" );
789 p_access
->pf_seek
= OutSeek
;
790 p_access
->pf_write
= Write
;
791 p_access
->p_sys
= (void *)p_sys
;
796 vlc_UrlClean( &p_sys
->url
);
797 vlc_tls_Delete( p_sys
->p_creds
);
803 /*****************************************************************************
804 * Close: free unused data structures
805 *****************************************************************************/
806 static void Close( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
808 msg_Dbg( p_access
, "stopping stream" );
809 ftp_StopStream( p_access
, p_sys
);
811 if( ftp_SendCommand( p_access
, p_sys
, "QUIT" ) < 0 )
813 msg_Warn( p_access
, "cannot quit" );
817 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
823 vlc_UrlClean( &p_sys
->url
);
824 vlc_tls_Delete( p_sys
->p_creds
);
828 static void InClose( vlc_object_t
*p_this
)
830 Close( p_this
, ((access_t
*)p_this
)->p_sys
);
834 static void OutClose( vlc_object_t
*p_this
)
836 Close( p_this
, GET_OUT_SYS(p_this
));
841 /*****************************************************************************
842 * Seek: try to go at the right place
843 *****************************************************************************/
844 static int _Seek( vlc_object_t
*p_access
, access_sys_t
*p_sys
, uint64_t i_pos
)
846 msg_Dbg( p_access
, "seeking to %"PRIu64
, i_pos
);
848 ftp_StopStream( (vlc_object_t
*)p_access
, p_sys
);
849 if( ftp_StartStream( (vlc_object_t
*)p_access
, p_sys
, i_pos
, false ) < 0 )
855 static int Seek( access_t
*p_access
, uint64_t i_pos
)
857 access_sys_t
*p_sys
= p_access
->p_sys
;
859 int val
= _Seek( (vlc_object_t
*)p_access
, p_sys
, i_pos
);
863 p_sys
->offset
= i_pos
;
869 static int OutSeek( sout_access_out_t
*p_access
, off_t i_pos
)
871 return _Seek( (vlc_object_t
*)p_access
, GET_OUT_SYS( p_access
), i_pos
);
875 /*****************************************************************************
877 *****************************************************************************/
878 static ssize_t
Read( access_t
*p_access
, void *p_buffer
, size_t i_len
)
880 access_sys_t
*p_sys
= p_access
->p_sys
;
882 assert( p_sys
->data
!= NULL
);
883 assert( !p_sys
->out
);
885 ssize_t i_read
= vlc_tls_Read( p_sys
->data
, p_buffer
, i_len
, false );
887 p_sys
->offset
+= i_read
;
888 else if( errno
!= EINTR
&& errno
!= EAGAIN
)
890 msg_Err( p_access
, "receive error: %s", vlc_strerror_c(errno
) );
897 /*****************************************************************************
899 *****************************************************************************/
900 static int DirRead (access_t
*p_access
, input_item_node_t
*p_current_node
)
902 access_sys_t
*p_sys
= p_access
->p_sys
;
903 int i_ret
= VLC_SUCCESS
;
905 assert( p_sys
->data
!= NULL
);
906 assert( !p_sys
->out
);
908 struct access_fsdir fsdir
;
909 access_fsdir_init( &fsdir
, p_access
, p_current_node
);
911 while (i_ret
== VLC_SUCCESS
)
914 int type
= ITEM_TYPE_UNKNOWN
;
916 char *psz_line
= vlc_tls_GetLine( p_sys
->data
);
917 if( psz_line
== NULL
)
920 if( p_sys
->features
.b_mlst
)
922 /* MLST Format is key=val;key=val...; FILENAME */
923 if( strstr( psz_line
, "type=dir" ) )
924 type
= ITEM_TYPE_DIRECTORY
;
925 if( strstr( psz_line
, "type=file" ) )
926 type
= ITEM_TYPE_FILE
;
928 /* Get the filename or fail */
929 psz_file
= strchr( psz_line
, ' ' );
934 msg_Warn( p_access
, "Empty filename in MLST list" );
943 char *psz_filename
= vlc_uri_encode( psz_file
);
944 if( psz_filename
!= NULL
&&
945 asprintf( &psz_uri
, "%s://%s:%d%s%s/%s",
946 ( p_sys
->tlsmode
== NONE
) ? "ftp" :
947 ( ( p_sys
->tlsmode
== IMPLICIT
) ? "ftps" : "ftpes" ),
948 p_sys
->url
.psz_host
, p_sys
->url
.i_port
,
949 p_sys
->url
.psz_path
? "/" : "",
950 p_sys
->url
.psz_path
? p_sys
->url
.psz_path
: "",
951 psz_filename
) != -1 )
953 i_ret
= access_fsdir_additem( &fsdir
, psz_uri
, psz_file
,
957 free( psz_filename
);
961 access_fsdir_finish( &fsdir
, i_ret
== VLC_SUCCESS
);
965 /*****************************************************************************
967 *****************************************************************************/
969 static ssize_t
Write( sout_access_out_t
*p_access
, block_t
*p_buffer
)
971 access_sys_t
*p_sys
= GET_OUT_SYS(p_access
);
974 assert( p_sys
->data
!= NULL
);
976 while( p_buffer
!= NULL
)
978 block_t
*p_next
= p_buffer
->p_next
;
980 i_write
+= vlc_tls_Write( p_sys
->data
,
981 p_buffer
->p_buffer
, p_buffer
->i_buffer
);
982 block_Release( p_buffer
);
991 /*****************************************************************************
993 *****************************************************************************/
994 static int Control( access_t
*p_access
, int i_query
, va_list args
)
996 access_sys_t
*sys
= p_access
->p_sys
;
1002 case STREAM_CAN_SEEK
:
1003 pb_bool
= va_arg( args
, bool * );
1006 case STREAM_CAN_FASTSEEK
:
1007 pb_bool
= va_arg( args
, bool * );
1010 case STREAM_CAN_PAUSE
:
1011 pb_bool
= va_arg( args
, bool * );
1012 *pb_bool
= true; /* FIXME */
1014 case STREAM_CAN_CONTROL_PACE
:
1015 pb_bool
= va_arg( args
, bool * );
1016 *pb_bool
= true; /* FIXME */
1018 case STREAM_GET_SIZE
:
1019 if( sys
->size
== UINT64_MAX
)
1020 return VLC_EGENERIC
;
1021 *va_arg( args
, uint64_t * ) = sys
->size
;
1024 case STREAM_GET_PTS_DELAY
:
1025 pi_64
= va_arg( args
, int64_t * );
1026 *pi_64
= INT64_C(1000)
1027 * var_InheritInteger( p_access
, "network-caching" );
1030 case STREAM_SET_PAUSE_STATE
:
1031 pb_bool
= va_arg( args
, bool * );
1033 return Seek( p_access
, sys
->offset
);
1037 return VLC_EGENERIC
;
1043 static int ftp_StartStream( vlc_object_t
*p_access
, access_sys_t
*p_sys
,
1044 uint64_t i_start
, bool b_directory
)
1046 char psz_ipv4
[16], *psz_ip
= p_sys
->sz_epsv_ip
;
1048 char *psz_arg
, *psz_parser
;
1051 assert( p_sys
->data
== NULL
);
1053 if( ( ftp_SendCommand( p_access
, p_sys
, *psz_ip
? "EPSV" : "PASV" ) < 0 )
1054 || ( ftp_RecvCommand( p_access
, p_sys
, &i_answer
, &psz_arg
) != 2 ) )
1056 msg_Err( p_access
, "cannot set passive mode" );
1057 return VLC_EGENERIC
;
1060 psz_parser
= strchr( psz_arg
, '(' );
1061 if( psz_parser
== NULL
)
1064 msg_Err( p_access
, "cannot parse passive mode response" );
1065 return VLC_EGENERIC
;
1070 if( sscanf( psz_parser
, "(%*3c%u", &i_port
) < 1 )
1073 msg_Err( p_access
, "cannot parse passive mode response" );
1074 return VLC_EGENERIC
;
1079 unsigned a1
, a2
, a3
, a4
, p1
, p2
;
1081 if( ( sscanf( psz_parser
, "(%u,%u,%u,%u,%u,%u", &a1
, &a2
, &a3
, &a4
,
1082 &p1
, &p2
) < 6 ) || ( a1
> 255 ) || ( a2
> 255 )
1083 || ( a3
> 255 ) || ( a4
> 255 ) || ( p1
> 255 ) || ( p2
> 255 ) )
1086 msg_Err( p_access
, "cannot parse passive mode response" );
1087 return VLC_EGENERIC
;
1090 sprintf( psz_ipv4
, "%u.%u.%u.%u", a1
, a2
, a3
, a4
);
1092 i_port
= (p1
<< 8) | p2
;
1096 msg_Dbg( p_access
, "ip:%s port:%d", psz_ip
, i_port
);
1098 if( ftp_SendCommand( p_access
, p_sys
, "TYPE I" ) < 0 ||
1099 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) != 2 )
1101 msg_Err( p_access
, "cannot set binary transfer mode" );
1102 return VLC_EGENERIC
;
1107 if( ftp_SendCommand( p_access
, p_sys
, "REST %"PRIu64
, i_start
) < 0 ||
1108 ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) > 3 )
1110 msg_Err( p_access
, "cannot set restart offset" );
1111 return VLC_EGENERIC
;
1115 msg_Dbg( p_access
, "waiting for data connection..." );
1116 p_sys
->data
= vlc_tls_SocketOpenTCP( p_access
, psz_ip
, i_port
);
1117 if( p_sys
->data
== NULL
)
1119 msg_Err( p_access
, "failed to connect with server" );
1120 return VLC_EGENERIC
;
1122 msg_Dbg( p_access
, "connection with \"%s:%d\" successful",
1127 if( p_sys
->features
.b_mlst
&&
1128 ftp_SendCommand( p_access
, p_sys
, "MLSD" ) >= 0 &&
1129 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) <= 2 )
1131 msg_Dbg( p_access
, "Using MLST extension to list" );
1134 if( ftp_SendCommand( p_access
, p_sys
, "NLST" ) < 0 ||
1135 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
) > 2 )
1137 msg_Err( p_access
, "cannot list directory contents" );
1138 return VLC_EGENERIC
;
1144 assert( p_sys
->url
.psz_path
);
1145 if( ftp_SendCommand( p_access
, p_sys
, "%s %s",
1146 p_sys
->out
? "STOR" : "RETR",
1147 p_sys
->url
.psz_path
) < 0
1148 || ftp_RecvCommand( p_access
, p_sys
, &i_answer
, NULL
) > 2 )
1150 msg_Err( p_access
, "cannot retrieve file" );
1151 return VLC_EGENERIC
;
1155 if( p_sys
->tlsmode
!= NONE
)
1157 /* FIXME: Do Reuse TLS Session */
1158 /* TLS/SSL handshake */
1159 vlc_tls_t
*secure
= vlc_tls_ClientSessionCreate( p_sys
->p_creds
,
1160 p_sys
->data
, p_sys
->url
.psz_host
,
1161 ( p_sys
->tlsmode
== EXPLICIT
) ? "ftpes-data"
1164 if( secure
== NULL
)
1166 msg_Err( p_access
, "cannot establish FTP/TLS session for data" \
1167 ": server not allowing new session ?" );
1168 return VLC_EGENERIC
;
1170 p_sys
->data
= secure
;
1176 static int ftp_StopStream ( vlc_object_t
*p_access
, access_sys_t
*p_sys
)
1178 int ret
= VLC_SUCCESS
;
1180 if( ftp_SendCommand( p_access
, p_sys
, "ABOR" ) < 0 )
1182 msg_Warn( p_access
, "cannot abort file" );
1186 if( p_sys
->data
!= NULL
)
1188 vlc_tls_Close( p_sys
->data
);
1191 if( ret
== VLC_SUCCESS
)
1192 /* Read the final response from RETR/STOR, i.e. 426 or 226 */
1193 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);
1196 if( ret
== VLC_SUCCESS
)
1197 /* Read the response from ABOR, i.e. 226 or 225 */
1198 ftp_RecvCommand( p_access
, p_sys
, NULL
, NULL
);