contrib: cargo: use cargo/vendored-openssl if needed
[vlc.git] / modules / access / unc.c
blob92a390535e796195f12a8ccb15a190d17269a445
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 *****************************************************************************/
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
27 #include <assert.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <sys/stat.h>
31 #include <io.h>
32 #include <windows.h>
33 #include <lm.h>
35 #include <vlc_common.h>
36 #include <vlc_fs.h>
37 #include <vlc_plugin.h>
38 #include <vlc_access.h>
39 #include <vlc_input_item.h>
40 #include <vlc_url.h>
41 #include <vlc_keystore.h>
42 #include <vlc_charset.h>
44 #include "smb_common.h"
46 typedef struct
48 int i_smb;
49 uint64_t size;
50 vlc_url_t url;
51 } access_sys_t;
53 static void Win32AddConnection(stream_t *access, const char *server,
54 const char *share, const char *user,
55 const char *pwd)
57 char *remote_name;
59 if (share != NULL)
60 { /* skip leading and remove trailing slashes */
61 int slen = strcspn(++share, "/");
63 if (asprintf(&remote_name, "\\\\%s\\%.*s", server, slen, share) < 0)
64 return;
66 else
68 if (asprintf(&remote_name, "\\\\%s\\", server) < 0)
69 return;
72 NETRESOURCE net_resource = {
73 .dwType = RESOURCETYPE_DISK,
74 .lpRemoteName = ToWide(remote_name),
77 free(remote_name);
79 const char *msg;
80 wchar_t *wpwd = pwd ? ToWide(pwd) : NULL;
81 wchar_t *wuser = user ? ToWide(user) : NULL;
83 switch (WNetAddConnection2(&net_resource, wpwd, wuser, 0))
85 case NO_ERROR:
86 msg = "connected to %ls";
87 break;
88 case ERROR_ALREADY_ASSIGNED:
89 case ERROR_DEVICE_ALREADY_REMEMBERED:
90 msg = "already connected to %ls";
91 break;
92 default:
93 msg = "failed to connect to %ls";
95 msg_Dbg(access, msg, net_resource.lpRemoteName);
96 free(net_resource.lpRemoteName);
97 free(wpwd);
98 free(wuser);
101 /* Build an SMB URI
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 )
108 assert(psz_server);
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 : ""
111 if( psz_user )
112 Win32AddConnection( p_access, psz_server, psz_share_path,
113 psz_user, psz_pwd );
114 return asprintf( ppsz_uri, "//%s%s%s%s", psz_server, PSZ_SHARE_PATH_OR_NULL,
115 PSZ_NAME_OR_NULL );
118 static int Seek( stream_t *p_access, uint64_t i_pos )
120 access_sys_t *p_sys = p_access->p_sys;
121 int64_t i_ret;
123 if( i_pos >= INT64_MAX )
124 return VLC_EGENERIC;
126 msg_Dbg( p_access, "seeking to %"PRId64, i_pos );
128 i_ret = lseek( p_sys->i_smb, i_pos, SEEK_SET );
129 if( i_ret == -1 )
131 msg_Err( p_access, "seek failed (%s)", vlc_strerror_c(errno) );
132 return VLC_EGENERIC;
135 return VLC_SUCCESS;
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;
141 int i_read;
143 i_read = read( p_sys->i_smb, p_buffer, i_len );
144 if( i_read < 0 )
146 msg_Err( p_access, "read failed (%s)", vlc_strerror_c(errno) );
147 i_read = 0;
150 return i_read;
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;
165 DWORD i_nb_elem;
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 )
170 return VLC_ENOMEM;
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 )
183 continue;
184 char* psz_name = FromWide( p_current->shi1_netname );
185 if( psz_name == NULL )
187 i_ret = VLC_ENOMEM;
188 break;
191 char* psz_path;
192 if( smb_get_uri( p_access, &psz_path, NULL, NULL,
193 p_sys->url.psz_host, p_sys->url.psz_path,
194 psz_name ) < 0 )
196 free( psz_name );
197 i_ret = VLC_ENOMEM;
198 break;
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
203 // to add "file:"
204 char* psz_uri;
205 if( asprintf( &psz_uri, "file:%s", psz_path ) < 0 )
207 free( psz_name );
208 free( psz_path );
209 i_ret = VLC_ENOMEM;
210 break;
212 free( psz_path );
214 i_ret = vlc_readdir_helper_additem( &rdh, psz_uri, NULL,
215 psz_name, ITEM_TYPE_DIRECTORY, ITEM_NET );
216 free( psz_name );
217 free( psz_uri );
220 NetApiBufferFree( p_info );
221 } while( i_share_enum_res == ERROR_MORE_DATA && i_ret == VLC_SUCCESS );
223 free( wpsz_host );
225 vlc_readdir_helper_finish( &rdh, i_ret == VLC_SUCCESS );
227 return i_ret;
230 static int Control( stream_t *p_access, int i_query, va_list args )
232 access_sys_t *sys = p_access->p_sys;
234 switch( i_query )
236 case STREAM_CAN_SEEK:
237 case STREAM_CAN_PAUSE:
238 case STREAM_CAN_CONTROL_PACE:
239 *va_arg( args, bool* ) = true;
240 break;
242 case STREAM_CAN_FASTSEEK:
243 *va_arg( args, bool* ) = false;
244 break;
246 case STREAM_GET_SIZE:
247 if( p_access->pf_readdir != NULL )
248 return VLC_EGENERIC;
249 *va_arg( args, uint64_t * ) = sys->size;
250 break;
252 case STREAM_GET_PTS_DELAY:
253 *va_arg( args, vlc_tick_t * ) = VLC_TICK_FROM_MS(
254 var_InheritInteger( p_access, "network-caching" ) );
255 break;
257 case STREAM_SET_PAUSE_STATE:
258 /* Nothing to do */
259 break;
261 default:
262 return VLC_EGENERIC;
265 return VLC_SUCCESS;
268 static int Open(vlc_object_t *obj)
270 stream_t *access = (stream_t *)obj;
271 vlc_url_t url;
272 vlc_credential credential;
273 char *psz_decoded_path = NULL, *psz_uri = NULL, *psz_var_domain = NULL;
274 int fd;
275 uint64_t size;
276 bool is_dir;
278 if (vlc_UrlParseFixup(&url, access->psz_url) != 0)
280 vlc_UrlClean(&url);
281 return VLC_EGENERIC;
284 if (url.psz_path != NULL)
286 psz_decoded_path = vlc_uri_decode_duplicate(url.psz_path);
287 if (psz_decoded_path == NULL)
289 vlc_UrlClean(&url);
290 return VLC_EGENERIC;
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);
299 for (;;)
301 struct stat st;
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);
310 vlc_UrlClean(&url);
311 return VLC_ENOMEM;
314 if (stat(psz_uri, &st) == 0)
316 is_dir = S_ISDIR(st.st_mode) != 0;
317 size = st.st_size;
318 break;
321 /* stat() fails with servers or shares. Assume directory. */
322 is_dir = true;
323 size = 0;
325 if (errno != EACCES)
326 break;
328 errno = 0;
329 if (!vlc_credential_get(&credential, access, "smb-user",
330 "smb-pwd", SMB_LOGIN_DIALOG_TITLE,
331 SMB_LOGIN_DIALOG_TEXT, url.psz_host))
332 break;
335 vlc_credential_store(&credential, access);
336 vlc_credential_clean(&credential);
337 free(psz_var_domain);
338 free(psz_decoded_path);
340 /* Init access */
341 access_sys_t *sys = vlc_obj_calloc(obj, 1, sizeof (*sys));
342 if (unlikely(sys == NULL))
344 free(psz_uri);
345 vlc_UrlClean(&url);
346 return VLC_ENOMEM;
349 access->p_sys = sys;
351 if (is_dir)
353 sys->url = url;
354 access->pf_readdir = DirRead;
355 access->pf_control = access_vaDirectoryControlHelper;
356 fd = -1;
358 else
360 access->pf_read = Read;
361 access->pf_control = Control;
362 access->pf_seek = Seek;
363 fd = open(psz_uri, O_RDONLY, 0);
364 vlc_UrlClean(&url);
366 free(psz_uri);
368 sys->size = size;
369 sys->i_smb = fd;
371 return VLC_SUCCESS;
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);
380 close(sys->i_smb);
383 vlc_module_begin()
384 set_shortname("UNC")
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)
393 add_shortcut("smb")
394 set_callbacks(Open, Close)
395 vlc_module_end()