]> git.sesse.net Git - vlc/blob - modules/access/sftp.c
DTV: do not fix up "0 kHz" to "0 Hz"
[vlc] / modules / access / sftp.c
1 /*****************************************************************************
2  * sftp.c: SFTP input module
3  *****************************************************************************
4  * Copyright (C) 2009 the VideoLAN team
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
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, 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 CACHING_TEXT N_("Caching value in ms")
52 #define CACHING_LONGTEXT N_( \
53   "Caching value for SFTP streams. This value should be set in milliseconds." )
54 #define USER_TEXT N_("SFTP user name")
55 #define USER_LONGTEXT N_("User name that will be used for the connection.")
56 #define PASS_TEXT N_("SFTP password")
57 #define PASS_LONGTEXT N_("Password that will be used for the connection.")
58 #define PORT_TEXT N_("SFTP port")
59 #define PORT_LONGTEXT N_("SFTP port number to use on the server")
60 #define MTU_TEXT N_("Read size")
61 #define MTU_LONGTEXT N_("Size of the request for reading access")
62
63 vlc_module_begin ()
64     set_shortname( "SFTP" )
65     set_description( N_("SFTP input") )
66     set_capability( "access", 0 )
67     set_category( CAT_INPUT )
68     set_subcategory( SUBCAT_INPUT_ACCESS )
69     add_integer( "sftp-caching", 2 * DEFAULT_PTS_DELAY / 1000,
70                      CACHING_TEXT, CACHING_LONGTEXT, true );
71     add_integer( "sftp-readsize", 8192, MTU_TEXT, MTU_LONGTEXT, true )
72     add_integer( "sftp-port", 22, PORT_TEXT, PORT_LONGTEXT, true )
73     add_shortcut( "sftp" )
74     set_callbacks( Open, Close )
75 vlc_module_end ()
76
77
78 /*****************************************************************************
79  * Local prototypes
80  *****************************************************************************/
81 static block_t* Block( access_t * );
82 static int      Seek( access_t *, uint64_t );
83 static int      Control( access_t *, int, va_list );
84
85
86 struct access_sys_t
87 {
88     int i_socket;
89     LIBSSH2_SESSION* ssh_session;
90     LIBSSH2_SFTP* sftp_session;
91     LIBSSH2_SFTP_HANDLE* file;
92     int i_read_size;
93 };
94
95
96
97 /**
98  * Connect to the sftp server and ask for a file
99  * @param p_this: the vlc_object
100  * @return VLC_SUCCESS if everything was fine
101  */
102 static int Open( vlc_object_t* p_this )
103 {
104     access_t*   p_access = (access_t*)p_this;
105     access_sys_t* p_sys;
106     char* psz_username = NULL;
107     char* psz_password = NULL;
108     int i_port;
109     int i_ret;
110     vlc_url_t url;
111     size_t i_len;
112     int i_type;
113
114     if( !p_access->psz_location )
115         return VLC_EGENERIC;
116
117     STANDARD_BLOCK_ACCESS_INIT;
118
119     /* Parse the URL */
120     const char* path = p_access->psz_location;
121     vlc_UrlParse( &url, path, 0 );
122
123     /* Check for some parameters */
124     if( EMPTY_STR( url.psz_host ) )
125     {
126         msg_Err( p_access, "You might give a non empty host" );
127         goto error;
128     }
129
130     /* If the user name is empty, ask the user */
131     if( !EMPTY_STR( url.psz_username ) && url.psz_password )
132     {
133         psz_username = strdup( url.psz_username );
134         psz_password = strdup( url.psz_password );
135     }
136     else
137     {
138         dialog_Login( p_access, &psz_username, &psz_password,
139                       _("SFTP authentication"),
140                       _("Please enter a valid login and password for the sftp "
141                         "connexion to %s"), url.psz_host );
142         if( EMPTY_STR(psz_username) || !psz_password )
143             goto error;
144     }
145
146     if( url.i_port <= 0 )
147         i_port = var_InheritInteger( p_access, "sftp-port" );
148     else
149         i_port = url.i_port;
150
151
152     /* Connect to the server using a regular socket */
153     p_sys->i_socket = net_Connect( p_access, url.psz_host, i_port, SOCK_STREAM, 0 );
154
155     /* Create the ssh connexion and wait until the server answer */
156     if( ( p_sys->ssh_session = libssh2_session_init() ) == NULL )
157         goto error;
158
159     while( ( i_ret = libssh2_session_startup( p_sys->ssh_session,
160                                               p_sys->i_socket ) )
161            == LIBSSH2_ERROR_EAGAIN );
162
163     if( i_ret != 0 )
164     {
165         msg_Err( p_access, "Impossible to open the connection to %s:%i", url.psz_host, i_port );
166         goto error;
167     }
168
169     /* Set the socket in non-blocking mode */
170     libssh2_session_set_blocking( p_sys->ssh_session, 1 );
171
172     /* List the know hosts */
173     LIBSSH2_KNOWNHOSTS *ssh_knownhosts = libssh2_knownhost_init( p_sys->ssh_session );
174     if( !ssh_knownhosts )
175         goto error;
176
177     char *psz_home = config_GetUserDir( VLC_HOME_DIR );
178     char *psz_knownhosts_file;
179     asprintf( &psz_knownhosts_file, "%s/.ssh/known_hosts", psz_home );
180     libssh2_knownhost_readfile( ssh_knownhosts, psz_knownhosts_file,
181                                 LIBSSH2_KNOWNHOST_FILE_OPENSSH );
182     free( psz_knownhosts_file );
183     free( psz_home );
184
185     const char *fingerprint = libssh2_session_hostkey( p_sys->ssh_session, &i_len, &i_type );
186     struct libssh2_knownhost *host;
187     int check = libssh2_knownhost_check( ssh_knownhosts, url.psz_host,
188                                          fingerprint, i_len,
189                                          LIBSSH2_KNOWNHOST_TYPE_PLAIN |
190                                          LIBSSH2_KNOWNHOST_KEYENC_RAW,
191                                          &host );
192
193     libssh2_knownhost_free( ssh_knownhosts );
194
195     /* Check that it does match or at least that the host is unkown */
196     switch(check)
197     {
198     case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
199     case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
200         msg_Dbg( p_access, "Unable to check the remote host" );
201         break;
202     case LIBSSH2_KNOWNHOST_CHECK_MATCH:
203         msg_Dbg( p_access, "Succesfuly matched the host" );
204         break;
205     case LIBSSH2_KNOWNHOST_CHECK_MISMATCH:
206         msg_Err( p_access, "The host does not match !! The remote key changed !!" );
207         goto error;
208     }
209
210     //TODO: ask for the available auth methods
211
212     /* send the login/password */
213     if( libssh2_userauth_password( p_sys->ssh_session, psz_username, psz_password ) )
214     {
215         msg_Err( p_access, "Authentication by password failed" );
216         goto error;
217     }
218
219     /* Create the sftp session */
220     p_sys->sftp_session = libssh2_sftp_init( p_sys->ssh_session );
221
222     if( !p_sys->sftp_session )
223     {
224         msg_Err( p_access, "Unable to initialize the SFTP session" );
225         goto error;
226     }
227
228     /* Open the given file */
229     p_sys->file = libssh2_sftp_open( p_sys->sftp_session, url.psz_path, LIBSSH2_FXF_READ, 0 );
230     if( !p_sys->file )
231     {
232         msg_Err( p_access, "Unable to open the remote file %s", url.psz_path );
233         goto error;
234     }
235
236     /* Get some information */
237     LIBSSH2_SFTP_ATTRIBUTES attributes;
238     if( libssh2_sftp_stat( p_sys->sftp_session, url.psz_path, &attributes ) )
239     {
240         msg_Err( p_access, "Impossible to get information about the remote file %s", url.psz_path );
241         goto error;
242     }
243     p_access->info.i_size = attributes.filesize;
244
245     /* Create the two variables */
246     var_Create( p_access, "sftp-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
247     p_sys->i_read_size = var_InheritInteger( p_access, "sftp-readsize" );
248
249     free( psz_password );
250     free( psz_username );
251     vlc_UrlClean( &url );
252     return VLC_SUCCESS;
253
254 error:
255     if( p_sys->ssh_session )
256         libssh2_session_free( p_sys->ssh_session );
257     free( psz_password );
258     free( psz_username );
259     vlc_UrlClean( &url );
260     free( p_sys );
261     return VLC_EGENERIC;
262 }
263
264
265 /* Close: quit the module */
266 static void Close( vlc_object_t* p_this )
267 {
268     access_t*   p_access = (access_t*)p_this;
269     access_sys_t* p_sys = p_access->p_sys;
270
271     libssh2_sftp_close_handle( p_sys->file );
272     libssh2_sftp_shutdown( p_sys->sftp_session );
273
274     libssh2_session_free( p_sys->ssh_session );
275     free( p_sys );
276 }
277
278
279 static block_t* Block( access_t* p_access )
280 {
281     if( p_access->info.b_eof )
282         return NULL;
283
284     /* Allocate the buffer we need */
285     size_t i_len = __MIN( p_access->p_sys->i_read_size, p_access->info.i_size -
286                                               p_access->info.i_pos );
287     block_t* p_block = block_New( p_access, i_len );
288     if( !p_block )
289         return NULL;
290
291     /* Read the specified size */
292     ssize_t i_ret = libssh2_sftp_read( p_access->p_sys->file, (char*)p_block->p_buffer, i_len );
293
294     if( i_ret < 0 )
295     {
296         block_Release( p_block );
297         msg_Err( p_access, "read failed" );
298         return NULL;
299     }
300     else if( i_ret == 0 )
301     {
302         p_access->info.b_eof = true;
303         block_Release( p_block );
304         return NULL;
305     }
306     else
307     {
308         p_access->info.i_pos += i_ret;
309         return p_block;
310     }
311 }
312
313
314 static int Seek( access_t* p_access, uint64_t i_pos )
315 {
316     p_access->info.i_pos = i_pos;
317     p_access->info.b_eof = false;
318
319     libssh2_sftp_seek( p_access->p_sys->file, i_pos );
320     return VLC_SUCCESS;
321 }
322
323
324 static int Control( access_t* p_access, int i_query, va_list args )
325 {
326     bool*       pb_bool;
327     int64_t*    pi_64;
328
329     switch( i_query )
330     {
331     case ACCESS_CAN_SEEK:
332         pb_bool = (bool*)va_arg( args, bool* );
333         *pb_bool = true;
334         break;
335
336     case ACCESS_CAN_FASTSEEK:
337         pb_bool = (bool*)va_arg( args, bool* );
338         *pb_bool = false;
339         break;
340
341     case ACCESS_CAN_PAUSE:
342     case ACCESS_CAN_CONTROL_PACE:
343         pb_bool = (bool*)va_arg( args, bool* );
344         *pb_bool = true;
345         break;
346
347     case ACCESS_GET_PTS_DELAY:
348         pi_64 = (int64_t*)va_arg( args, int64_t* );
349         *pi_64 = var_GetInteger( p_access, "sftp-caching" ) * INT64_C(1000);
350         break;
351
352     case ACCESS_SET_PAUSE_STATE:
353         break;
354
355     case ACCESS_GET_TITLE_INFO:
356     case ACCESS_SET_TITLE:
357     case ACCESS_SET_SEEKPOINT:
358     case ACCESS_SET_PRIVATE_ID_STATE:
359     case ACCESS_GET_META:
360     case ACCESS_GET_PRIVATE_ID_STATE:
361     case ACCESS_GET_CONTENT_TYPE:
362         return VLC_EGENERIC;
363
364     default:
365         msg_Warn( p_access, "unimplemented query %d in control", i_query );
366         return VLC_EGENERIC;
367     }
368
369     return VLC_SUCCESS;
370 }
371