demux: avi: invalidate skipped chunks
[vlc.git] / modules / access / smb.c
blob4075259f1787fa98e2a1d2046bb5c72466fc0e22
1 /*****************************************************************************
2 * smb.c: SMB input module
3 *****************************************************************************
4 * Copyright (C) 2001-2015 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Gildas Bazin <gbazin@videolan.org>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <assert.h>
32 #include <errno.h>
33 #ifdef _WIN32
34 # include <fcntl.h>
35 # include <sys/stat.h>
36 # include <io.h>
37 # define smbc_open(a,b,c) vlc_open(a,b,c)
38 # define smbc_stat(a,b) _stati64(a,b)
39 # define smbc_read read
40 # define smbc_lseek _lseeki64
41 # define smbc_close close
42 #else
43 # include <libsmbclient.h>
44 #endif
46 #include <vlc_common.h>
47 #include <vlc_fs.h>
48 #include <vlc_plugin.h>
49 #include <vlc_access.h>
50 #include <vlc_input_item.h>
51 #include <vlc_url.h>
52 #include <vlc_keystore.h>
54 #include "smb_common.h"
56 /*****************************************************************************
57 * Module descriptor
58 *****************************************************************************/
59 static int Open ( vlc_object_t * );
60 static void Close( vlc_object_t * );
62 #define SMB_HELP N_("Samba (Windows network shares) input")
63 vlc_module_begin ()
64 set_shortname( "SMB" )
65 set_description( N_("SMB input") )
66 set_help(SMB_HELP)
67 set_capability( "access", 0 )
68 set_category( CAT_INPUT )
69 set_subcategory( SUBCAT_INPUT_ACCESS )
70 add_string( "smb-user", NULL, SMB_USER_TEXT, SMB_USER_LONGTEXT,
71 false )
72 add_password( "smb-pwd", NULL, SMB_PASS_TEXT,
73 SMB_PASS_LONGTEXT, false )
74 add_string( "smb-domain", NULL, SMB_DOMAIN_TEXT,
75 SMB_DOMAIN_LONGTEXT, false )
76 add_shortcut( "smb" )
77 set_callbacks( Open, Close )
78 vlc_module_end ()
80 /*****************************************************************************
81 * Local prototypes
82 *****************************************************************************/
83 static ssize_t Read( access_t *, void *, size_t );
84 static int Seek( access_t *, uint64_t );
85 static int Control( access_t *, int, va_list );
86 #ifndef _WIN32
87 static int DirRead( access_t *, input_item_node_t * );
88 #endif
90 struct access_sys_t
92 int i_smb;
93 uint64_t size;
94 vlc_url_t url;
97 #ifdef _WIN32
98 static void Win32AddConnection( access_t *, const char *, const char *, const char *, const char *, const char * );
99 #else
100 static void smb_auth( const char *srv, const char *shr, char *wg, int wglen,
101 char *un, int unlen, char *pw, int pwlen )
103 VLC_UNUSED(srv);
104 VLC_UNUSED(shr);
105 VLC_UNUSED(wg);
106 VLC_UNUSED(wglen);
107 VLC_UNUSED(un);
108 VLC_UNUSED(unlen);
109 VLC_UNUSED(pw);
110 VLC_UNUSED(pwlen);
111 //wglen = unlen = pwlen = 0;
113 #endif
115 /* Build an SMB URI
116 * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] */
117 static int smb_get_uri( access_t *p_access, char **ppsz_uri,
118 const char *psz_domain,
119 const char *psz_user, const char *psz_pwd,
120 const char *psz_server, const char *psz_share_path,
121 const char *psz_name )
123 assert(psz_server);
124 #define PSZ_SHARE_PATH_OR_NULL psz_share_path ? psz_share_path : ""
125 #define PSZ_NAME_OR_NULL psz_name ? "/" : "", psz_name ? psz_name : ""
126 #ifdef _WIN32
127 if( psz_user )
128 Win32AddConnection( p_access, psz_server, psz_share_path,
129 psz_user, psz_pwd, psz_domain );
130 return asprintf( ppsz_uri, "//%s%s%s%s", psz_server, PSZ_SHARE_PATH_OR_NULL,
131 PSZ_NAME_OR_NULL );
132 #else
133 (void) p_access;
134 if( psz_user )
135 return asprintf( ppsz_uri, "smb://%s%s%s%s%s@%s%s%s%s",
136 psz_domain ? psz_domain : "", psz_domain ? ";" : "",
137 psz_user, psz_pwd ? ":" : "",
138 psz_pwd ? psz_pwd : "", psz_server,
139 PSZ_SHARE_PATH_OR_NULL, PSZ_NAME_OR_NULL );
140 else
141 return asprintf( ppsz_uri, "smb://%s%s%s%s", psz_server,
142 PSZ_SHARE_PATH_OR_NULL, PSZ_NAME_OR_NULL );
143 #endif
146 /****************************************************************************
147 * Open: connect to smb server and ask for file
148 ****************************************************************************/
149 static int Open( vlc_object_t *p_this )
151 access_t *p_access = (access_t*)p_this;
152 access_sys_t *p_sys;
153 struct stat filestat;
154 vlc_url_t url;
155 vlc_credential credential;
156 char *psz_decoded_path = NULL, *psz_uri = NULL,
157 *psz_var_domain = NULL;
158 int i_ret;
159 int i_smb;
160 uint64_t i_size;
161 bool b_is_dir = false;
163 #ifndef _WIN32
164 if( smbc_init( smb_auth, 0 ) )
165 return VLC_EGENERIC;
166 #endif
169 ** some version of glibc defines open as a macro, causing havoc
170 ** with other macros using 'open' under the hood, such as the
171 ** following one:
173 #if defined(smbc_open) && defined(open)
174 # undef open
175 #endif
177 vlc_UrlParse( &url, p_access->psz_url );
178 if( url.psz_path )
180 psz_decoded_path = vlc_uri_decode_duplicate( url.psz_path );
181 if( !psz_decoded_path )
183 vlc_UrlClean( &url );
184 return VLC_EGENERIC;
188 vlc_credential_init( &credential, &url );
189 psz_var_domain = var_InheritString( p_access, "smb-domain" );
190 credential.psz_realm = psz_var_domain;
191 vlc_credential_get( &credential, p_access, "smb-user", "smb-pwd",
192 NULL, NULL );
193 for (;;)
195 if( smb_get_uri( p_access, &psz_uri, credential.psz_realm,
196 credential.psz_username, credential.psz_password,
197 url.psz_host, psz_decoded_path, NULL ) == -1 )
199 vlc_credential_clean( &credential );
200 free(psz_var_domain);
201 free( psz_decoded_path );
202 vlc_UrlClean( &url );
203 return VLC_ENOMEM;
206 if( ( i_ret = smbc_stat( psz_uri, &filestat ) ) && errno == EACCES )
208 errno = 0;
209 if( vlc_credential_get( &credential, p_access, "smb-user", "smb-pwd",
210 SMB_LOGIN_DIALOG_TITLE,
211 SMB_LOGIN_DIALOG_TEXT, url.psz_host) )
212 continue;
215 /* smbc_stat fails with servers or shares. Assume they are directory */
216 if( i_ret || S_ISDIR( filestat.st_mode ) )
217 b_is_dir = true;
218 break;
221 vlc_credential_store( &credential, p_access );
222 vlc_credential_clean( &credential );
223 free(psz_var_domain);
224 free( psz_decoded_path );
226 /* Init p_access */
227 p_sys =
228 p_access->p_sys = vlc_calloc( p_this, 1, sizeof( access_sys_t ) );
229 if( !p_sys )
231 free( psz_uri );
232 vlc_UrlClean( &url );
233 return VLC_ENOMEM;
236 if( b_is_dir )
238 #ifdef _WIN32
239 free( psz_uri );
240 vlc_UrlClean( &url );
241 return VLC_EGENERIC;
242 #else
243 p_sys->url = url;
244 p_access->pf_readdir = DirRead;
245 p_access->pf_control = access_vaDirectoryControlHelper;
246 i_smb = smbc_opendir( psz_uri );
247 i_size = 0;
248 if( i_smb < 0 )
249 vlc_UrlClean( &p_sys->url );
250 #endif
252 else
254 ACCESS_SET_CALLBACKS( Read, NULL, Control, Seek );
255 i_smb = smbc_open( psz_uri, O_RDONLY, 0 );
256 i_size = filestat.st_size;
257 vlc_UrlClean( &url );
259 free( psz_uri );
261 if( i_smb < 0 )
263 msg_Err( p_access, "open failed for '%s' (%s)",
264 p_access->psz_location, vlc_strerror_c(errno) );
265 return VLC_EGENERIC;
268 p_sys->size = i_size;
269 p_sys->i_smb = i_smb;
271 return VLC_SUCCESS;
274 /*****************************************************************************
275 * Close: free unused data structures
276 *****************************************************************************/
277 static void Close( vlc_object_t *p_this )
279 access_t *p_access = (access_t*)p_this;
280 access_sys_t *p_sys = p_access->p_sys;
282 vlc_UrlClean( &p_sys->url );
284 #ifndef _WIN32
285 if( p_access->pf_readdir )
286 smbc_closedir( p_sys->i_smb );
287 else
288 #endif
289 smbc_close( p_sys->i_smb );
292 /*****************************************************************************
293 * Seek: try to go at the right place
294 *****************************************************************************/
295 static int Seek( access_t *p_access, uint64_t i_pos )
297 access_sys_t *p_sys = p_access->p_sys;
298 int64_t i_ret;
300 if( i_pos >= INT64_MAX )
301 return VLC_EGENERIC;
303 msg_Dbg( p_access, "seeking to %"PRId64, i_pos );
305 i_ret = smbc_lseek( p_sys->i_smb, i_pos, SEEK_SET );
306 if( i_ret == -1 )
308 msg_Err( p_access, "seek failed (%s)", vlc_strerror_c(errno) );
309 return VLC_EGENERIC;
312 return VLC_SUCCESS;
315 /*****************************************************************************
316 * Read:
317 *****************************************************************************/
318 static ssize_t Read( access_t *p_access, void *p_buffer, size_t i_len )
320 access_sys_t *p_sys = p_access->p_sys;
321 int i_read;
323 i_read = smbc_read( p_sys->i_smb, p_buffer, i_len );
324 if( i_read < 0 )
326 msg_Err( p_access, "read failed (%s)", vlc_strerror_c(errno) );
327 i_read = 0;
330 return i_read;
333 #ifndef _WIN32
334 /*****************************************************************************
335 * DirRead:
336 *****************************************************************************/
337 static int DirRead (access_t *p_access, input_item_node_t *p_node )
339 access_sys_t *p_sys = p_access->p_sys;
340 struct smbc_dirent *p_entry;
341 int i_ret = VLC_SUCCESS;
343 struct access_fsdir fsdir;
344 access_fsdir_init( &fsdir, p_access, p_node );
346 while( i_ret == VLC_SUCCESS && ( p_entry = smbc_readdir( p_sys->i_smb ) ) )
348 char *psz_uri;
349 const char *psz_server = p_sys->url.psz_host;
350 const char *psz_path = p_sys->url.psz_path;
351 const char *psz_name = p_entry->name;
352 int i_type;
354 switch( p_entry->smbc_type )
356 case SMBC_SERVER:
357 case SMBC_WORKGROUP:
358 psz_server = p_sys->url.psz_host;
359 psz_path = NULL;
360 psz_name = NULL;
361 case SMBC_FILE_SHARE:
362 case SMBC_DIR:
363 i_type = ITEM_TYPE_DIRECTORY;
364 break;
365 case SMBC_FILE:
366 i_type = ITEM_TYPE_FILE;
367 break;
368 default:
369 case SMBC_PRINTER_SHARE:
370 case SMBC_COMMS_SHARE:
371 case SMBC_IPC_SHARE:
372 case SMBC_LINK:
373 continue;
376 char *psz_encoded_name = NULL;
377 if( psz_name != NULL
378 && ( psz_encoded_name = vlc_uri_encode( psz_name ) ) == NULL )
380 i_ret = VLC_ENOMEM;
381 break;
383 if( smb_get_uri( p_access, &psz_uri, NULL, NULL, NULL,
384 psz_server, psz_path, psz_encoded_name ) < 0 )
386 free(psz_encoded_name);
387 i_ret = VLC_ENOMEM;
388 break;
390 free(psz_encoded_name);
391 i_ret = access_fsdir_additem( &fsdir, psz_uri, p_entry->name,
392 i_type, ITEM_NET );
393 free( psz_uri );
396 access_fsdir_finish( &fsdir, i_ret == VLC_SUCCESS );
398 return i_ret;
400 #endif
402 /*****************************************************************************
403 * Control:
404 *****************************************************************************/
405 static int Control( access_t *p_access, int i_query, va_list args )
407 access_sys_t *sys = p_access->p_sys;
409 switch( i_query )
411 case STREAM_CAN_SEEK:
412 case STREAM_CAN_PAUSE:
413 case STREAM_CAN_CONTROL_PACE:
414 *va_arg( args, bool* ) = true;
415 break;
417 case STREAM_CAN_FASTSEEK:
418 *va_arg( args, bool* ) = false;
419 break;
421 case STREAM_GET_SIZE:
422 if( p_access->pf_readdir != NULL )
423 return VLC_EGENERIC;
424 *va_arg( args, uint64_t * ) = sys->size;
425 break;
427 case STREAM_GET_PTS_DELAY:
428 *va_arg( args, int64_t * ) = INT64_C(1000)
429 * var_InheritInteger( p_access, "network-caching" );
430 break;
432 case STREAM_SET_PAUSE_STATE:
433 /* Nothing to do */
434 break;
436 default:
437 return VLC_EGENERIC;
440 return VLC_SUCCESS;
443 #ifdef _WIN32
444 static void Win32AddConnection( access_t *p_access, const char *psz_server,
445 const char *psz_share, const char *psz_user,
446 const char *psz_pwd, const char *psz_domain )
448 char psz_remote[MAX_PATH];
449 NETRESOURCE net_resource;
450 DWORD i_result;
451 VLC_UNUSED( psz_domain );
453 memset( &net_resource, 0, sizeof(net_resource) );
454 net_resource.dwType = RESOURCETYPE_DISK;
456 if (psz_share)
457 psz_share = psz_share + 1; /* skip first '/' */
458 else
459 psz_share = "";
461 snprintf( psz_remote, sizeof( psz_remote ), "\\\\%s\\%s", psz_server, psz_share );
462 /* remove trailings '/' */
463 char *psz_delim = strchr( psz_remote, '/' );
464 if( psz_delim )
465 *psz_delim = '\0';
467 net_resource.lpRemoteName = psz_remote;
469 i_result = WNetAddConnection2( &net_resource, psz_pwd, psz_user, 0 );
471 if( i_result != NO_ERROR )
473 msg_Dbg( p_access, "connected to %s", psz_remote );
475 else if( i_result != ERROR_ALREADY_ASSIGNED &&
476 i_result != ERROR_DEVICE_ALREADY_REMEMBERED )
478 msg_Dbg( p_access, "already connected to %s", psz_remote );
480 else
482 msg_Dbg( p_access, "failed to connect to %s", psz_remote );
485 #endif // _WIN32