1 /*****************************************************************************
2 * unc.c: Microsoft Windows Universal Naming Convention input module
3 *****************************************************************************
4 * Copyright (C) 2001-2015 VLC authors and VideoLAN
6 * Authors: Gildas Bazin <gbazin@videolan.org>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
35 #include <vlc_common.h>
37 #include <vlc_plugin.h>
38 #include <vlc_access.h>
39 #include <vlc_input_item.h>
41 #include <vlc_keystore.h>
42 #include <vlc_charset.h>
44 #include "smb_common.h"
53 static void Win32AddConnection(stream_t
*access
, const char *server
,
54 const char *share
, const char *user
,
60 { /* skip leading and remove trailing slashes */
61 int slen
= strcspn(++share
, "/");
63 if (asprintf(&remote_name
, "\\\\%s\\%.*s", server
, slen
, share
) < 0)
68 if (asprintf(&remote_name
, "\\\\%s\\", server
) < 0)
72 NETRESOURCE net_resource
= {
73 .dwType
= RESOURCETYPE_DISK
,
74 .lpRemoteName
= ToWide(remote_name
),
80 wchar_t *wpwd
= pwd
? ToWide(pwd
) : NULL
;
81 wchar_t *wuser
= user
? ToWide(user
) : NULL
;
83 switch (WNetAddConnection2(&net_resource
, wpwd
, wuser
, 0))
86 msg
= "connected to %ls";
88 case ERROR_ALREADY_ASSIGNED
:
89 case ERROR_DEVICE_ALREADY_REMEMBERED
:
90 msg
= "already connected to %ls";
93 msg
= "failed to connect to %ls";
95 msg_Dbg(access
, msg
, net_resource
.lpRemoteName
);
96 free(net_resource
.lpRemoteName
);
102 * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] */
103 static int smb_get_uri( stream_t
*p_access
, char **ppsz_uri
,
104 const char *psz_user
, const char *psz_pwd
,
105 const char *psz_server
, const char *psz_share_path
,
106 const char *psz_name
)
109 #define PSZ_SHARE_PATH_OR_NULL psz_share_path ? psz_share_path : ""
110 #define PSZ_NAME_OR_NULL psz_name ? "/" : "", psz_name ? psz_name : ""
112 Win32AddConnection( p_access
, psz_server
, psz_share_path
,
114 return asprintf( ppsz_uri
, "//%s%s%s%s", psz_server
, PSZ_SHARE_PATH_OR_NULL
,
118 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
120 access_sys_t
*p_sys
= p_access
->p_sys
;
123 if( i_pos
>= INT64_MAX
)
126 msg_Dbg( p_access
, "seeking to %"PRId64
, i_pos
);
128 i_ret
= lseek( p_sys
->i_smb
, i_pos
, SEEK_SET
);
131 msg_Err( p_access
, "seek failed (%s)", vlc_strerror_c(errno
) );
138 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
140 access_sys_t
*p_sys
= p_access
->p_sys
;
143 i_read
= read( p_sys
->i_smb
, p_buffer
, i_len
);
146 msg_Err( p_access
, "read failed (%s)", vlc_strerror_c(errno
) );
153 static int DirRead(stream_t
*p_access
, input_item_node_t
*p_node
)
155 access_sys_t
*p_sys
= p_access
->p_sys
;
156 int i_ret
= VLC_SUCCESS
;
158 struct vlc_readdir_helper rdh
;
159 vlc_readdir_helper_init( &rdh
, p_access
, p_node
);
161 // Handle share listing from here. Directory browsing is handled by the
162 // usual filesystem module.
163 SHARE_INFO_1
*p_info
;
164 DWORD i_share_enum_res
;
166 DWORD i_resume_handle
= 0;
167 DWORD i_total_elements
; // Unused, but needs to be passed
168 wchar_t *wpsz_host
= ToWide( p_sys
->url
.psz_host
);
169 if( wpsz_host
== NULL
)
173 i_share_enum_res
= NetShareEnum( wpsz_host
, 1, (LPBYTE
*)&p_info
,
174 MAX_PREFERRED_LENGTH
, &i_nb_elem
,
175 &i_total_elements
, &i_resume_handle
);
176 if( i_share_enum_res
== ERROR_SUCCESS
||
177 i_share_enum_res
== ERROR_MORE_DATA
)
179 for ( DWORD i
= 0; i
< i_nb_elem
; ++i
)
181 SHARE_INFO_1
*p_current
= p_info
+ i
;
182 if( p_current
->shi1_type
& STYPE_SPECIAL
)
184 char* psz_name
= FromWide( p_current
->shi1_netname
);
185 if( psz_name
== NULL
)
192 if( smb_get_uri( p_access
, &psz_path
, NULL
, NULL
,
193 p_sys
->url
.psz_host
, p_sys
->url
.psz_path
,
200 // We need to concatenate the scheme before, as the window version
201 // of smb_get_uri generates a path (and the other call site needs
202 // a path). The path is already prefixed by "//" so we just need
205 if( asprintf( &psz_uri
, "file:%s", psz_path
) < 0 )
214 i_ret
= vlc_readdir_helper_additem( &rdh
, psz_uri
, NULL
,
215 psz_name
, ITEM_TYPE_DIRECTORY
, ITEM_NET
);
220 NetApiBufferFree( p_info
);
221 } while( i_share_enum_res
== ERROR_MORE_DATA
&& i_ret
== VLC_SUCCESS
);
225 vlc_readdir_helper_finish( &rdh
, i_ret
== VLC_SUCCESS
);
230 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
232 access_sys_t
*sys
= p_access
->p_sys
;
236 case STREAM_CAN_SEEK
:
237 case STREAM_CAN_PAUSE
:
238 case STREAM_CAN_CONTROL_PACE
:
239 *va_arg( args
, bool* ) = true;
242 case STREAM_CAN_FASTSEEK
:
243 *va_arg( args
, bool* ) = false;
246 case STREAM_GET_SIZE
:
247 if( p_access
->pf_readdir
!= NULL
)
249 *va_arg( args
, uint64_t * ) = sys
->size
;
252 case STREAM_GET_PTS_DELAY
:
253 *va_arg( args
, vlc_tick_t
* ) = VLC_TICK_FROM_MS(
254 var_InheritInteger( p_access
, "network-caching" ) );
257 case STREAM_SET_PAUSE_STATE
:
268 static int Open(vlc_object_t
*obj
)
270 stream_t
*access
= (stream_t
*)obj
;
272 vlc_credential credential
;
273 char *psz_decoded_path
= NULL
, *psz_uri
= NULL
, *psz_var_domain
= NULL
;
278 if (vlc_UrlParseFixup(&url
, access
->psz_url
) != 0)
284 if (url
.psz_path
!= NULL
)
286 psz_decoded_path
= vlc_uri_decode_duplicate(url
.psz_path
);
287 if (psz_decoded_path
== NULL
)
294 vlc_credential_init(&credential
, &url
);
295 psz_var_domain
= var_InheritString(access
, "smb-domain");
296 credential
.psz_realm
= psz_var_domain
;
297 vlc_credential_get(&credential
, access
, "smb-user", "smb-pwd", NULL
, NULL
);
303 if (smb_get_uri(access
, &psz_uri
,
304 credential
.psz_username
, credential
.psz_password
,
305 url
.psz_host
, psz_decoded_path
, NULL
) == -1 )
307 vlc_credential_clean(&credential
);
308 free(psz_var_domain
);
309 free(psz_decoded_path
);
314 if (stat(psz_uri
, &st
) == 0)
316 is_dir
= S_ISDIR(st
.st_mode
) != 0;
321 /* stat() fails with servers or shares. Assume directory. */
329 if (!vlc_credential_get(&credential
, access
, "smb-user",
330 "smb-pwd", SMB_LOGIN_DIALOG_TITLE
,
331 SMB_LOGIN_DIALOG_TEXT
, url
.psz_host
))
335 vlc_credential_store(&credential
, access
);
336 vlc_credential_clean(&credential
);
337 free(psz_var_domain
);
338 free(psz_decoded_path
);
341 access_sys_t
*sys
= vlc_obj_calloc(obj
, 1, sizeof (*sys
));
342 if (unlikely(sys
== NULL
))
354 access
->pf_readdir
= DirRead
;
355 access
->pf_control
= access_vaDirectoryControlHelper
;
360 access
->pf_read
= Read
;
361 access
->pf_control
= Control
;
362 access
->pf_seek
= Seek
;
363 fd
= open(psz_uri
, O_RDONLY
, 0);
374 static void Close(vlc_object_t
*obj
)
376 stream_t
*access
= (stream_t
*)obj
;
377 access_sys_t
*sys
= access
->p_sys
;
379 vlc_UrlClean(&sys
->url
);
385 set_description(N_("UNC input"))
386 set_help(N_("Microsoft Windows networking (UNC) input"))
387 set_capability("access", 0)
388 set_category(CAT_INPUT
)
389 set_subcategory(SUBCAT_INPUT_ACCESS
)
390 add_string("smb-user", NULL
, SMB_USER_TEXT
, SMB_USER_LONGTEXT
, false)
391 add_password("smb-pwd", NULL
, SMB_PASS_TEXT
, SMB_PASS_LONGTEXT
)
392 add_string("smb-domain", NULL
, SMB_DOMAIN_TEXT
, SMB_DOMAIN_LONGTEXT
, false)
394 set_callbacks(Open
, Close
)