]> git.sesse.net Git - vlc/blob - modules/access/sftp.c
DCP: update authors
[vlc] / modules / access / sftp.c
1 /*****************************************************************************
2  * sftp.c: SFTP input module
3  *****************************************************************************
4  * Copyright (C) 2009 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: RĂ©mi Duraffort <ivoire@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33
34 #include <assert.h>
35
36 #include <vlc_access.h>
37 #include <vlc_dialog.h>
38 #include <vlc_network.h>
39 #include <vlc_url.h>
40
41 #include <libssh2.h>
42 #include <libssh2_sftp.h>
43
44
45 /*****************************************************************************
46  * Module descriptor
47  *****************************************************************************/
48 static int  Open ( vlc_object_t* );
49 static void Close( vlc_object_t* );
50
51 #define PORT_TEXT N_("SFTP port")
52 #define PORT_LONGTEXT N_("SFTP port number to use on the server")
53 #define MTU_TEXT N_("Read size")
54 #define MTU_LONGTEXT N_("Size of the request for reading access")
55
56 vlc_module_begin ()
57     set_shortname( "SFTP" )
58     set_description( N_("SFTP input") )
59     set_capability( "access", 0 )
60     set_category( CAT_INPUT )
61     set_subcategory( SUBCAT_INPUT_ACCESS )
62     add_integer( "sftp-readsize", 8192, MTU_TEXT, MTU_LONGTEXT, true )
63     add_integer( "sftp-port", 22, PORT_TEXT, PORT_LONGTEXT, true )
64     add_shortcut( "sftp" )
65     set_callbacks( Open, Close )
66 vlc_module_end ()
67
68
69 /*****************************************************************************
70  * Local prototypes
71  *****************************************************************************/
72 static block_t* Block( access_t * );
73 static int      Seek( access_t *, uint64_t );
74 static int      Control( access_t *, int, va_list );
75
76
77 struct access_sys_t
78 {
79     int i_socket;
80     LIBSSH2_SESSION* ssh_session;
81     LIBSSH2_SFTP* sftp_session;
82     LIBSSH2_SFTP_HANDLE* file;
83     uint64_t filesize;
84     size_t i_read_size;
85 };
86
87
88
89 /**
90  * Connect to the sftp server and ask for a file
91  * @param p_this: the vlc_object
92  * @return VLC_SUCCESS if everything was fine
93  */
94 static int Open( vlc_object_t* p_this )
95 {
96     access_t*   p_access = (access_t*)p_this;
97     access_sys_t* p_sys;
98     char* psz_username = NULL;
99     char* psz_password = NULL;
100     int i_port;
101     int i_ret;
102     vlc_url_t url;
103     size_t i_len;
104     int i_type;
105
106     if( !p_access->psz_location )
107         return VLC_EGENERIC;
108
109     STANDARD_BLOCK_ACCESS_INIT;
110
111     /* Parse the URL */
112     const char* path = p_access->psz_location;
113     vlc_UrlParse( &url, path, 0 );
114
115     /* Check for some parameters */
116     if( EMPTY_STR( url.psz_host ) )
117     {
118         msg_Err( p_access, "You might give a non empty host" );
119         goto error;
120     }
121
122     /* If the user name is empty, ask the user */
123     if( !EMPTY_STR( url.psz_username ) && url.psz_password )
124     {
125         psz_username = strdup( url.psz_username );
126         psz_password = strdup( url.psz_password );
127     }
128     else
129     {
130         dialog_Login( p_access, &psz_username, &psz_password,
131                       _("SFTP authentication"),
132                       _("Please enter a valid login and password for the sftp "
133                         "connexion to %s"), url.psz_host );
134         if( EMPTY_STR(psz_username) || !psz_password )
135             goto error;
136     }
137
138     if( url.i_port <= 0 )
139         i_port = var_InheritInteger( p_access, "sftp-port" );
140     else
141         i_port = url.i_port;
142
143
144     /* Connect to the server using a regular socket */
145     p_sys->i_socket = net_Connect( p_access, url.psz_host, i_port, SOCK_STREAM, 0 );
146
147     /* Create the ssh connexion and wait until the server answer */
148     if( ( p_sys->ssh_session = libssh2_session_init() ) == NULL )
149         goto error;
150
151     while( ( i_ret = libssh2_session_startup( p_sys->ssh_session,
152                                               p_sys->i_socket ) )
153            == LIBSSH2_ERROR_EAGAIN );
154
155     if( i_ret != 0 )
156     {
157         msg_Err( p_access, "Impossible to open the connection to %s:%i", url.psz_host, i_port );
158         goto error;
159     }
160
161     /* Set the socket in non-blocking mode */
162     libssh2_session_set_blocking( p_sys->ssh_session, 1 );
163
164     /* List the know hosts */
165     LIBSSH2_KNOWNHOSTS *ssh_knownhosts = libssh2_knownhost_init( p_sys->ssh_session );
166     if( !ssh_knownhosts )
167         goto error;
168
169     char *psz_home = config_GetUserDir( VLC_HOME_DIR );
170     char *psz_knownhosts_file;
171     if( asprintf( &psz_knownhosts_file, "%s/.ssh/known_hosts", psz_home ) != -1 )
172     {
173         libssh2_knownhost_readfile( ssh_knownhosts, psz_knownhosts_file,
174                 LIBSSH2_KNOWNHOST_FILE_OPENSSH );
175         free( psz_knownhosts_file );
176     }
177     free( psz_home );
178
179     const char *fingerprint = libssh2_session_hostkey( p_sys->ssh_session, &i_len, &i_type );
180     struct libssh2_knownhost *host;
181     int check = libssh2_knownhost_check( ssh_knownhosts, url.psz_host,
182                                          fingerprint, i_len,
183                                          LIBSSH2_KNOWNHOST_TYPE_PLAIN |
184                                          LIBSSH2_KNOWNHOST_KEYENC_RAW,
185                                          &host );
186
187     libssh2_knownhost_free( ssh_knownhosts );
188
189     /* Check that it does match or at least that the host is unknown */
190     switch(check)
191     {
192     case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
193     case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
194         msg_Dbg( p_access, "Unable to check the remote host" );
195         break;
196     case LIBSSH2_KNOWNHOST_CHECK_MATCH:
197         msg_Dbg( p_access, "Succesfuly matched the host" );
198         break;
199     case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
200         msg_Err( p_access, "The host does not match !! The remote key changed !!" );
201         goto error;
202     }
203
204     //TODO: ask for the available auth methods
205
206     /* send the login/password */
207     if( libssh2_userauth_password( p_sys->ssh_session, psz_username, psz_password ) )
208     {
209         msg_Err( p_access, "Authentication by password failed" );
210         goto error;
211     }
212
213     /* Create the sftp session */
214     p_sys->sftp_session = libssh2_sftp_init( p_sys->ssh_session );
215
216     if( !p_sys->sftp_session )
217     {
218         msg_Err( p_access, "Unable to initialize the SFTP session" );
219         goto error;
220     }
221
222     /* Open the given file */
223     p_sys->file = libssh2_sftp_open( p_sys->sftp_session, url.psz_path, LIBSSH2_FXF_READ, 0 );
224     if( !p_sys->file )
225     {
226         msg_Err( p_access, "Unable to open the remote file %s", url.psz_path );
227         goto error;
228     }
229
230     /* Get some information */
231     LIBSSH2_SFTP_ATTRIBUTES attributes;
232     if( libssh2_sftp_stat( p_sys->sftp_session, url.psz_path, &attributes ) )
233     {
234         msg_Err( p_access, "Impossible to get information about the remote file %s", url.psz_path );
235         goto error;
236     }
237     p_sys->filesize = attributes.filesize;
238
239     p_sys->i_read_size = var_InheritInteger( p_access, "sftp-readsize" );
240
241     free( psz_password );
242     free( psz_username );
243     vlc_UrlClean( &url );
244     return VLC_SUCCESS;
245
246 error:
247     if( p_sys->ssh_session )
248         libssh2_session_free( p_sys->ssh_session );
249     free( psz_password );
250     free( psz_username );
251     vlc_UrlClean( &url );
252     free( p_sys );
253     return VLC_EGENERIC;
254 }
255
256
257 /* Close: quit the module */
258 static void Close( vlc_object_t* p_this )
259 {
260     access_t*   p_access = (access_t*)p_this;
261     access_sys_t* p_sys = p_access->p_sys;
262
263     libssh2_sftp_close_handle( p_sys->file );
264     libssh2_sftp_shutdown( p_sys->sftp_session );
265
266     libssh2_session_free( p_sys->ssh_session );
267     free( p_sys );
268 }
269
270
271 static block_t* Block( access_t* p_access )
272 {
273     access_sys_t *p_sys = p_access->p_sys;
274
275     if( p_access->info.b_eof )
276         return NULL;
277
278     /* Allocate the buffer we need */
279     size_t i_len = __MIN( p_sys->i_read_size,
280                           p_sys->filesize - p_access->info.i_pos );
281     block_t* p_block = block_Alloc( i_len );
282     if( !p_block )
283         return NULL;
284
285     /* Read the specified size */
286     ssize_t i_ret = libssh2_sftp_read( p_access->p_sys->file, (char*)p_block->p_buffer, i_len );
287
288     if( i_ret < 0 )
289     {
290         block_Release( p_block );
291         msg_Err( p_access, "read failed" );
292         return NULL;
293     }
294     else if( i_ret == 0 )
295     {
296         p_access->info.b_eof = true;
297         block_Release( p_block );
298         return NULL;
299     }
300     else
301     {
302         p_access->info.i_pos += i_ret;
303         return p_block;
304     }
305 }
306
307
308 static int Seek( access_t* p_access, uint64_t i_pos )
309 {
310     p_access->info.i_pos = i_pos;
311     p_access->info.b_eof = false;
312
313     libssh2_sftp_seek( p_access->p_sys->file, i_pos );
314     return VLC_SUCCESS;
315 }
316
317
318 static int Control( access_t* p_access, int i_query, va_list args )
319 {
320     bool*       pb_bool;
321     int64_t*    pi_64;
322
323     switch( i_query )
324     {
325     case ACCESS_CAN_SEEK:
326         pb_bool = (bool*)va_arg( args, bool* );
327         *pb_bool = true;
328         break;
329
330     case ACCESS_CAN_FASTSEEK:
331         pb_bool = (bool*)va_arg( args, bool* );
332         *pb_bool = false;
333         break;
334
335     case ACCESS_CAN_PAUSE:
336     case ACCESS_CAN_CONTROL_PACE:
337         pb_bool = (bool*)va_arg( args, bool* );
338         *pb_bool = true;
339         break;
340
341     case ACCESS_GET_SIZE:
342         *va_arg( args, uint64_t * ) = p_access->p_sys->filesize;
343         break;
344
345     case ACCESS_GET_PTS_DELAY:
346         pi_64 = (int64_t*)va_arg( args, int64_t* );
347         *pi_64 = INT64_C(1000)
348                * var_InheritInteger( p_access, "network-caching" );
349         break;
350
351     case ACCESS_SET_PAUSE_STATE:
352         break;
353
354     default:
355         return VLC_EGENERIC;
356     }
357
358     return VLC_SUCCESS;
359 }
360