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>
39 # define smbc_open(a,b,c) vlc_open(a,b,c)
40 # define smbc_stat(a,b) _stati64(a,b)
41 # define smbc_read read
42 # define smbc_lseek _lseeki64
43 # define smbc_close close
45 # include <libsmbclient.h>
48 #include <vlc_common.h>
50 #include <vlc_plugin.h>
51 #include <vlc_access.h>
52 #include <vlc_input_item.h>
54 #include <vlc_keystore.h>
55 #include <vlc_charset.h>
57 #include "smb_common.h"
59 /*****************************************************************************
61 *****************************************************************************/
62 static int Open ( vlc_object_t
* );
63 static void Close( vlc_object_t
* );
65 #define SMB_HELP N_("Samba (Windows network shares) input")
67 set_shortname( "SMB" )
68 set_description( N_("SMB input") )
70 set_capability( "access", 0 )
71 set_category( CAT_INPUT
)
72 set_subcategory( SUBCAT_INPUT_ACCESS
)
73 add_string( "smb-user", NULL
, SMB_USER_TEXT
, SMB_USER_LONGTEXT
,
75 add_password( "smb-pwd", NULL
, SMB_PASS_TEXT
,
76 SMB_PASS_LONGTEXT
, false )
77 add_string( "smb-domain", NULL
, SMB_DOMAIN_TEXT
,
78 SMB_DOMAIN_LONGTEXT
, false )
80 set_callbacks( Open
, Close
)
83 /*****************************************************************************
85 *****************************************************************************/
86 static ssize_t
Read( stream_t
*, void *, size_t );
87 static int Seek( stream_t
*, uint64_t );
88 static int Control( stream_t
*, int, va_list );
89 static int DirRead( stream_t
*, input_item_node_t
* );
99 static void Win32AddConnection( stream_t
*, const char *, const char *, const char *, const char *, const char * );
101 static void smb_auth( const char *srv
, const char *shr
, char *wg
, int wglen
,
102 char *un
, int unlen
, char *pw
, int pwlen
)
112 //wglen = unlen = pwlen = 0;
117 * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] */
118 static int smb_get_uri( stream_t
*p_access
, char **ppsz_uri
,
119 const char *psz_domain
,
120 const char *psz_user
, const char *psz_pwd
,
121 const char *psz_server
, const char *psz_share_path
,
122 const char *psz_name
)
125 #define PSZ_SHARE_PATH_OR_NULL psz_share_path ? psz_share_path : ""
126 #define PSZ_NAME_OR_NULL psz_name ? "/" : "", psz_name ? psz_name : ""
129 Win32AddConnection( p_access
, psz_server
, psz_share_path
,
130 psz_user
, psz_pwd
, psz_domain
);
131 return asprintf( ppsz_uri
, "//%s%s%s%s", psz_server
, PSZ_SHARE_PATH_OR_NULL
,
136 return asprintf( ppsz_uri
, "smb://%s%s%s%s%s@%s%s%s%s",
137 psz_domain
? psz_domain
: "", psz_domain
? ";" : "",
138 psz_user
, psz_pwd
? ":" : "",
139 psz_pwd
? psz_pwd
: "", psz_server
,
140 PSZ_SHARE_PATH_OR_NULL
, PSZ_NAME_OR_NULL
);
142 return asprintf( ppsz_uri
, "smb://%s%s%s%s", psz_server
,
143 PSZ_SHARE_PATH_OR_NULL
, PSZ_NAME_OR_NULL
);
147 /****************************************************************************
148 * Open: connect to smb server and ask for file
149 ****************************************************************************/
150 static int Open( vlc_object_t
*p_this
)
152 stream_t
*p_access
= (stream_t
*)p_this
;
154 struct stat filestat
;
156 vlc_credential credential
;
157 char *psz_decoded_path
= NULL
, *psz_uri
= NULL
,
158 *psz_var_domain
= NULL
;
162 bool b_is_dir
= false;
165 if( smbc_init( smb_auth
, 0 ) )
170 ** some version of glibc defines open as a macro, causing havoc
171 ** with other macros using 'open' under the hood, such as the
174 #if defined(smbc_open) && defined(open)
178 if( vlc_UrlParseFixup( &url
, p_access
->psz_url
) != 0 )
180 vlc_UrlClean( &url
);
185 psz_decoded_path
= vlc_uri_decode_duplicate( url
.psz_path
);
186 if( !psz_decoded_path
)
188 vlc_UrlClean( &url
);
193 vlc_credential_init( &credential
, &url
);
194 psz_var_domain
= var_InheritString( p_access
, "smb-domain" );
195 credential
.psz_realm
= psz_var_domain
;
196 vlc_credential_get( &credential
, p_access
, "smb-user", "smb-pwd",
200 if( smb_get_uri( p_access
, &psz_uri
, credential
.psz_realm
,
201 credential
.psz_username
, credential
.psz_password
,
202 url
.psz_host
, psz_decoded_path
, NULL
) == -1 )
204 vlc_credential_clean( &credential
);
205 free(psz_var_domain
);
206 free( psz_decoded_path
);
207 vlc_UrlClean( &url
);
211 if( ( i_ret
= smbc_stat( psz_uri
, &filestat
) ) && errno
== EACCES
)
214 if( vlc_credential_get( &credential
, p_access
, "smb-user", "smb-pwd",
215 SMB_LOGIN_DIALOG_TITLE
,
216 SMB_LOGIN_DIALOG_TEXT
, url
.psz_host
) )
220 /* smbc_stat fails with servers or shares. Assume they are directory */
221 if( i_ret
|| S_ISDIR( filestat
.st_mode
) )
226 vlc_credential_store( &credential
, p_access
);
227 vlc_credential_clean( &credential
);
228 free(psz_var_domain
);
229 free( psz_decoded_path
);
233 p_access
->p_sys
= vlc_obj_calloc( p_this
, 1, sizeof( access_sys_t
) );
237 vlc_UrlClean( &url
);
244 p_access
->pf_readdir
= DirRead
;
245 p_access
->pf_control
= access_vaDirectoryControlHelper
;
248 i_smb
= smbc_opendir( psz_uri
);
250 vlc_UrlClean( &p_sys
->url
);
255 ACCESS_SET_CALLBACKS( Read
, NULL
, Control
, Seek
);
256 i_smb
= smbc_open( psz_uri
, O_RDONLY
, 0 );
257 i_size
= filestat
.st_size
;
258 vlc_UrlClean( &url
);
265 msg_Err( p_access
, "open failed for '%s' (%s)",
266 p_access
->psz_location
, vlc_strerror_c(errno
) );
271 p_sys
->size
= i_size
;
272 p_sys
->i_smb
= i_smb
;
277 /*****************************************************************************
278 * Close: free unused data structures
279 *****************************************************************************/
280 static void Close( vlc_object_t
*p_this
)
282 stream_t
*p_access
= (stream_t
*)p_this
;
283 access_sys_t
*p_sys
= p_access
->p_sys
;
285 vlc_UrlClean( &p_sys
->url
);
288 if( p_access
->pf_readdir
)
289 smbc_closedir( p_sys
->i_smb
);
292 smbc_close( p_sys
->i_smb
);
295 /*****************************************************************************
296 * Seek: try to go at the right place
297 *****************************************************************************/
298 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
300 access_sys_t
*p_sys
= p_access
->p_sys
;
303 if( i_pos
>= INT64_MAX
)
306 msg_Dbg( p_access
, "seeking to %"PRId64
, i_pos
);
308 i_ret
= smbc_lseek( p_sys
->i_smb
, i_pos
, SEEK_SET
);
311 msg_Err( p_access
, "seek failed (%s)", vlc_strerror_c(errno
) );
318 /*****************************************************************************
320 *****************************************************************************/
321 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
323 access_sys_t
*p_sys
= p_access
->p_sys
;
326 i_read
= smbc_read( p_sys
->i_smb
, p_buffer
, i_len
);
329 msg_Err( p_access
, "read failed (%s)", vlc_strerror_c(errno
) );
336 /*****************************************************************************
338 *****************************************************************************/
339 static int DirRead (stream_t
*p_access
, input_item_node_t
*p_node
)
341 access_sys_t
*p_sys
= p_access
->p_sys
;
342 int i_ret
= VLC_SUCCESS
;
344 struct vlc_readdir_helper rdh
;
345 vlc_readdir_helper_init( &rdh
, p_access
, p_node
);
348 struct smbc_dirent
*p_entry
;
350 while( i_ret
== VLC_SUCCESS
&& ( p_entry
= smbc_readdir( p_sys
->i_smb
) ) )
353 const char *psz_server
= p_sys
->url
.psz_host
;
354 const char *psz_path
= p_sys
->url
.psz_path
;
355 const char *psz_name
= p_entry
->name
;
358 switch( p_entry
->smbc_type
)
362 psz_server
= p_sys
->url
.psz_host
;
365 case SMBC_FILE_SHARE
:
367 i_type
= ITEM_TYPE_DIRECTORY
;
370 i_type
= ITEM_TYPE_FILE
;
373 case SMBC_PRINTER_SHARE
:
374 case SMBC_COMMS_SHARE
:
380 char *psz_encoded_name
= NULL
;
382 && ( psz_encoded_name
= vlc_uri_encode( psz_name
) ) == NULL
)
387 if( smb_get_uri( p_access
, &psz_uri
, NULL
, NULL
, NULL
,
388 psz_server
, psz_path
, psz_encoded_name
) < 0 )
390 free(psz_encoded_name
);
394 free(psz_encoded_name
);
395 i_ret
= vlc_readdir_helper_additem( &rdh
, psz_uri
, NULL
, p_entry
->name
,
400 // Handle share listing from here. Directory browsing is handled by the
401 // usual filesystem module.
402 SHARE_INFO_1
*p_info
;
403 DWORD i_share_enum_res
;
405 DWORD i_resume_handle
= 0;
406 DWORD i_total_elements
; // Unused, but needs to be passed
407 wchar_t *wpsz_host
= ToWide( p_sys
->url
.psz_host
);
408 if( wpsz_host
== NULL
)
412 i_share_enum_res
= NetShareEnum( wpsz_host
, 1, (LPBYTE
*)&p_info
,
413 MAX_PREFERRED_LENGTH
, &i_nb_elem
,
414 &i_total_elements
, &i_resume_handle
);
415 if( i_share_enum_res
== ERROR_SUCCESS
||
416 i_share_enum_res
== ERROR_MORE_DATA
)
418 for ( DWORD i
= 0; i
< i_nb_elem
; ++i
)
420 SHARE_INFO_1
*p_current
= p_info
+ i
;
421 if( p_current
->shi1_type
& STYPE_SPECIAL
)
423 char* psz_name
= FromWide( p_current
->shi1_netname
);
424 if( psz_name
== NULL
)
431 if( smb_get_uri( p_access
, &psz_path
, NULL
, NULL
, NULL
,
432 p_sys
->url
.psz_host
, p_sys
->url
.psz_path
,
439 // We need to concatenate the scheme before, as the window version
440 // of smb_get_uri generates a path (and the other call site needs
441 // a path). The path is already prefixed by "//" so we just need
444 if( asprintf( &psz_uri
, "file:%s", psz_path
) < 0 )
453 i_ret
= vlc_readdir_helper_additem( &rdh
, psz_uri
, NULL
,
454 psz_name
, ITEM_TYPE_DIRECTORY
, ITEM_NET
);
459 NetApiBufferFree( p_info
);
460 } while( i_share_enum_res
== ERROR_MORE_DATA
&& i_ret
== VLC_SUCCESS
);
465 vlc_readdir_helper_finish( &rdh
, i_ret
== VLC_SUCCESS
);
470 /*****************************************************************************
472 *****************************************************************************/
473 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
475 access_sys_t
*sys
= p_access
->p_sys
;
479 case STREAM_CAN_SEEK
:
480 case STREAM_CAN_PAUSE
:
481 case STREAM_CAN_CONTROL_PACE
:
482 *va_arg( args
, bool* ) = true;
485 case STREAM_CAN_FASTSEEK
:
486 *va_arg( args
, bool* ) = false;
489 case STREAM_GET_SIZE
:
490 if( p_access
->pf_readdir
!= NULL
)
492 *va_arg( args
, uint64_t * ) = sys
->size
;
495 case STREAM_GET_PTS_DELAY
:
496 *va_arg( args
, int64_t * ) = INT64_C(1000)
497 * var_InheritInteger( p_access
, "network-caching" );
500 case STREAM_SET_PAUSE_STATE
:
512 static void Win32AddConnection( stream_t
*p_access
, const char *psz_server
,
513 const char *psz_share
, const char *psz_user
,
514 const char *psz_pwd
, const char *psz_domain
)
516 char psz_remote
[MAX_PATH
];
517 NETRESOURCE net_resource
;
519 VLC_UNUSED( psz_domain
);
521 memset( &net_resource
, 0, sizeof(net_resource
) );
522 net_resource
.dwType
= RESOURCETYPE_DISK
;
525 psz_share
= psz_share
+ 1; /* skip first '/' */
529 snprintf( psz_remote
, sizeof( psz_remote
), "\\\\%s\\%s", psz_server
, psz_share
);
530 /* remove trailings '/' */
531 char *psz_delim
= strchr( psz_remote
, '/' );
535 net_resource
.lpRemoteName
= psz_remote
;
537 i_result
= WNetAddConnection2( &net_resource
, psz_pwd
, psz_user
, 0 );
539 if( i_result
!= NO_ERROR
)
541 msg_Dbg( p_access
, "connected to %s", psz_remote
);
543 else if( i_result
!= ERROR_ALREADY_ASSIGNED
&&
544 i_result
!= ERROR_DEVICE_ALREADY_REMEMBERED
)
546 msg_Dbg( p_access
, "already connected to %s", psz_remote
);
550 msg_Dbg( p_access
, "failed to connect to %s", psz_remote
);