1 /*****************************************************************************
2 * smb.c: SMB input module
3 *****************************************************************************
4 * Copyright (C) 2001-2015 VLC authors and VideoLAN
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 /*****************************************************************************
26 *****************************************************************************/
35 # include <sys/stat.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
43 # include <libsmbclient.h>
46 #include <vlc_common.h>
48 #include <vlc_plugin.h>
49 #include <vlc_access.h>
50 #include <vlc_input_item.h>
52 #include <vlc_keystore.h>
54 #include "smb_common.h"
56 /*****************************************************************************
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")
64 set_shortname( "SMB" )
65 set_description( N_("SMB input") )
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
,
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 )
77 set_callbacks( Open
, Close
)
80 /*****************************************************************************
82 *****************************************************************************/
83 static ssize_t
Read( stream_t
*, void *, size_t );
84 static int Seek( stream_t
*, uint64_t );
85 static int Control( stream_t
*, int, va_list );
87 static int DirRead( stream_t
*, input_item_node_t
* );
98 static void Win32AddConnection( stream_t
*, const char *, const char *, const char *, const char *, const char * );
100 static void smb_auth( const char *srv
, const char *shr
, char *wg
, int wglen
,
101 char *un
, int unlen
, char *pw
, int pwlen
)
111 //wglen = unlen = pwlen = 0;
116 * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] */
117 static int smb_get_uri( stream_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
)
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 : ""
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
,
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
);
141 return asprintf( ppsz_uri
, "smb://%s%s%s%s", psz_server
,
142 PSZ_SHARE_PATH_OR_NULL
, PSZ_NAME_OR_NULL
);
146 /****************************************************************************
147 * Open: connect to smb server and ask for file
148 ****************************************************************************/
149 static int Open( vlc_object_t
*p_this
)
151 stream_t
*p_access
= (stream_t
*)p_this
;
153 struct stat filestat
;
155 vlc_credential credential
;
156 char *psz_decoded_path
= NULL
, *psz_uri
= NULL
,
157 *psz_var_domain
= NULL
;
161 bool b_is_dir
= false;
164 if( smbc_init( smb_auth
, 0 ) )
169 ** some version of glibc defines open as a macro, causing havoc
170 ** with other macros using 'open' under the hood, such as the
173 #if defined(smbc_open) && defined(open)
177 vlc_UrlParse( &url
, p_access
->psz_url
);
180 psz_decoded_path
= vlc_uri_decode_duplicate( url
.psz_path
);
181 if( !psz_decoded_path
)
183 vlc_UrlClean( &url
);
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",
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
);
206 if( ( i_ret
= smbc_stat( psz_uri
, &filestat
) ) && errno
== EACCES
)
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
) )
215 /* smbc_stat fails with servers or shares. Assume they are directory */
216 if( i_ret
|| S_ISDIR( filestat
.st_mode
) )
221 vlc_credential_store( &credential
, p_access
);
222 vlc_credential_clean( &credential
);
223 free(psz_var_domain
);
224 free( psz_decoded_path
);
228 p_access
->p_sys
= vlc_calloc( p_this
, 1, sizeof( access_sys_t
) );
232 vlc_UrlClean( &url
);
240 vlc_UrlClean( &url
);
244 p_access
->pf_readdir
= DirRead
;
245 p_access
->pf_control
= access_vaDirectoryControlHelper
;
246 i_smb
= smbc_opendir( psz_uri
);
249 vlc_UrlClean( &p_sys
->url
);
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
);
263 msg_Err( p_access
, "open failed for '%s' (%s)",
264 p_access
->psz_location
, vlc_strerror_c(errno
) );
268 p_sys
->size
= i_size
;
269 p_sys
->i_smb
= i_smb
;
274 /*****************************************************************************
275 * Close: free unused data structures
276 *****************************************************************************/
277 static void Close( vlc_object_t
*p_this
)
279 stream_t
*p_access
= (stream_t
*)p_this
;
280 access_sys_t
*p_sys
= p_access
->p_sys
;
282 vlc_UrlClean( &p_sys
->url
);
285 if( p_access
->pf_readdir
)
286 smbc_closedir( p_sys
->i_smb
);
289 smbc_close( p_sys
->i_smb
);
292 /*****************************************************************************
293 * Seek: try to go at the right place
294 *****************************************************************************/
295 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
297 access_sys_t
*p_sys
= p_access
->p_sys
;
300 if( i_pos
>= INT64_MAX
)
303 msg_Dbg( p_access
, "seeking to %"PRId64
, i_pos
);
305 i_ret
= smbc_lseek( p_sys
->i_smb
, i_pos
, SEEK_SET
);
308 msg_Err( p_access
, "seek failed (%s)", vlc_strerror_c(errno
) );
315 /*****************************************************************************
317 *****************************************************************************/
318 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
320 access_sys_t
*p_sys
= p_access
->p_sys
;
323 i_read
= smbc_read( p_sys
->i_smb
, p_buffer
, i_len
);
326 msg_Err( p_access
, "read failed (%s)", vlc_strerror_c(errno
) );
334 /*****************************************************************************
336 *****************************************************************************/
337 static int DirRead (stream_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 vlc_readdir_helper rdh
;
344 vlc_readdir_helper_init( &rdh
, p_access
, p_node
);
346 while( i_ret
== VLC_SUCCESS
&& ( p_entry
= smbc_readdir( p_sys
->i_smb
) ) )
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
;
354 switch( p_entry
->smbc_type
)
358 psz_server
= p_sys
->url
.psz_host
;
361 case SMBC_FILE_SHARE
:
363 i_type
= ITEM_TYPE_DIRECTORY
;
366 i_type
= ITEM_TYPE_FILE
;
369 case SMBC_PRINTER_SHARE
:
370 case SMBC_COMMS_SHARE
:
376 char *psz_encoded_name
= NULL
;
378 && ( psz_encoded_name
= vlc_uri_encode( psz_name
) ) == NULL
)
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
);
390 free(psz_encoded_name
);
391 i_ret
= vlc_readdir_helper_additem( &rdh
, psz_uri
, NULL
, p_entry
->name
,
396 vlc_readdir_helper_finish( &rdh
, i_ret
== VLC_SUCCESS
);
402 /*****************************************************************************
404 *****************************************************************************/
405 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
407 access_sys_t
*sys
= p_access
->p_sys
;
411 case STREAM_CAN_SEEK
:
412 case STREAM_CAN_PAUSE
:
413 case STREAM_CAN_CONTROL_PACE
:
414 *va_arg( args
, bool* ) = true;
417 case STREAM_CAN_FASTSEEK
:
418 *va_arg( args
, bool* ) = false;
421 case STREAM_GET_SIZE
:
422 if( p_access
->pf_readdir
!= NULL
)
424 *va_arg( args
, uint64_t * ) = sys
->size
;
427 case STREAM_GET_PTS_DELAY
:
428 *va_arg( args
, int64_t * ) = INT64_C(1000)
429 * var_InheritInteger( p_access
, "network-caching" );
432 case STREAM_SET_PAUSE_STATE
:
444 static void Win32AddConnection( stream_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
;
451 VLC_UNUSED( psz_domain
);
453 memset( &net_resource
, 0, sizeof(net_resource
) );
454 net_resource
.dwType
= RESOURCETYPE_DISK
;
457 psz_share
= psz_share
+ 1; /* skip first '/' */
461 snprintf( psz_remote
, sizeof( psz_remote
), "\\\\%s\\%s", psz_server
, psz_share
);
462 /* remove trailings '/' */
463 char *psz_delim
= strchr( psz_remote
, '/' );
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
);
482 msg_Dbg( p_access
, "failed to connect to %s", psz_remote
);