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
,
57 NETRESOURCE net_resource
;
58 char remote_name
[MAX_PATH
];
60 memset(&net_resource
, 0, sizeof (net_resource
));
61 net_resource
.dwType
= RESOURCETYPE_DISK
;
63 snprintf(remote_name
, sizeof (remote_name
), "\\\\%s\\%s", server
,
64 (share
!= NULL
) ? share
+ 1 /* skip leading '/' */: "");
66 /* remove trailings '/' */
67 char *delim
= strchr(remote_name
, '/');
72 net_resource
.lpRemoteName
= ToWide(remote_name
);
74 wchar_t *wpwd
= pwd
? ToWide(pwd
) : NULL
;
75 wchar_t *wuser
= user
? ToWide(user
) : NULL
;
77 switch (WNetAddConnection2(&net_resource
, wpwd
, wuser
, 0))
80 msg
= "connected to %s";
82 case ERROR_ALREADY_ASSIGNED
:
83 case ERROR_DEVICE_ALREADY_REMEMBERED
:
84 msg
= "already connected to %s";
87 msg
= "failed to connect to %s";
89 free(net_resource
.lpRemoteName
);
92 msg_Dbg(access
, msg
, remote_name
);
96 * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] */
97 static int smb_get_uri( stream_t
*p_access
, char **ppsz_uri
,
98 const char *psz_user
, const char *psz_pwd
,
99 const char *psz_server
, const char *psz_share_path
,
100 const char *psz_name
)
103 #define PSZ_SHARE_PATH_OR_NULL psz_share_path ? psz_share_path : ""
104 #define PSZ_NAME_OR_NULL psz_name ? "/" : "", psz_name ? psz_name : ""
106 Win32AddConnection( p_access
, psz_server
, psz_share_path
,
108 return asprintf( ppsz_uri
, "//%s%s%s%s", psz_server
, PSZ_SHARE_PATH_OR_NULL
,
112 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
114 access_sys_t
*p_sys
= p_access
->p_sys
;
117 if( i_pos
>= INT64_MAX
)
120 msg_Dbg( p_access
, "seeking to %"PRId64
, i_pos
);
122 i_ret
= lseek( p_sys
->i_smb
, i_pos
, SEEK_SET
);
125 msg_Err( p_access
, "seek failed (%s)", vlc_strerror_c(errno
) );
132 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
134 access_sys_t
*p_sys
= p_access
->p_sys
;
137 i_read
= read( p_sys
->i_smb
, p_buffer
, i_len
);
140 msg_Err( p_access
, "read failed (%s)", vlc_strerror_c(errno
) );
147 static int DirRead(stream_t
*p_access
, input_item_node_t
*p_node
)
149 access_sys_t
*p_sys
= p_access
->p_sys
;
150 int i_ret
= VLC_SUCCESS
;
152 struct vlc_readdir_helper rdh
;
153 vlc_readdir_helper_init( &rdh
, p_access
, p_node
);
155 // Handle share listing from here. Directory browsing is handled by the
156 // usual filesystem module.
157 SHARE_INFO_1
*p_info
;
158 DWORD i_share_enum_res
;
160 DWORD i_resume_handle
= 0;
161 DWORD i_total_elements
; // Unused, but needs to be passed
162 wchar_t *wpsz_host
= ToWide( p_sys
->url
.psz_host
);
163 if( wpsz_host
== NULL
)
167 i_share_enum_res
= NetShareEnum( wpsz_host
, 1, (LPBYTE
*)&p_info
,
168 MAX_PREFERRED_LENGTH
, &i_nb_elem
,
169 &i_total_elements
, &i_resume_handle
);
170 if( i_share_enum_res
== ERROR_SUCCESS
||
171 i_share_enum_res
== ERROR_MORE_DATA
)
173 for ( DWORD i
= 0; i
< i_nb_elem
; ++i
)
175 SHARE_INFO_1
*p_current
= p_info
+ i
;
176 if( p_current
->shi1_type
& STYPE_SPECIAL
)
178 char* psz_name
= FromWide( p_current
->shi1_netname
);
179 if( psz_name
== NULL
)
186 if( smb_get_uri( p_access
, &psz_path
, NULL
, NULL
,
187 p_sys
->url
.psz_host
, p_sys
->url
.psz_path
,
194 // We need to concatenate the scheme before, as the window version
195 // of smb_get_uri generates a path (and the other call site needs
196 // a path). The path is already prefixed by "//" so we just need
199 if( asprintf( &psz_uri
, "file:%s", psz_path
) < 0 )
208 i_ret
= vlc_readdir_helper_additem( &rdh
, psz_uri
, NULL
,
209 psz_name
, ITEM_TYPE_DIRECTORY
, ITEM_NET
);
214 NetApiBufferFree( p_info
);
215 } while( i_share_enum_res
== ERROR_MORE_DATA
&& i_ret
== VLC_SUCCESS
);
219 vlc_readdir_helper_finish( &rdh
, i_ret
== VLC_SUCCESS
);
224 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
226 access_sys_t
*sys
= p_access
->p_sys
;
230 case STREAM_CAN_SEEK
:
231 case STREAM_CAN_PAUSE
:
232 case STREAM_CAN_CONTROL_PACE
:
233 *va_arg( args
, bool* ) = true;
236 case STREAM_CAN_FASTSEEK
:
237 *va_arg( args
, bool* ) = false;
240 case STREAM_GET_SIZE
:
241 if( p_access
->pf_readdir
!= NULL
)
243 *va_arg( args
, uint64_t * ) = sys
->size
;
246 case STREAM_GET_PTS_DELAY
:
247 *va_arg( args
, vlc_tick_t
* ) = VLC_TICK_FROM_MS(
248 var_InheritInteger( p_access
, "network-caching" ) );
251 case STREAM_SET_PAUSE_STATE
:
262 static int Open(vlc_object_t
*obj
)
264 stream_t
*access
= (stream_t
*)obj
;
266 vlc_credential credential
;
267 char *psz_decoded_path
= NULL
, *psz_uri
= NULL
, *psz_var_domain
= NULL
;
272 if (vlc_UrlParseFixup(&url
, access
->psz_url
) != 0)
278 if (url
.psz_path
!= NULL
)
280 psz_decoded_path
= vlc_uri_decode_duplicate(url
.psz_path
);
281 if (psz_decoded_path
== NULL
)
288 vlc_credential_init(&credential
, &url
);
289 psz_var_domain
= var_InheritString(access
, "smb-domain");
290 credential
.psz_realm
= psz_var_domain
;
291 vlc_credential_get(&credential
, access
, "smb-user", "smb-pwd", NULL
, NULL
);
297 if (smb_get_uri(access
, &psz_uri
,
298 credential
.psz_username
, credential
.psz_password
,
299 url
.psz_host
, psz_decoded_path
, NULL
) == -1 )
301 vlc_credential_clean(&credential
);
302 free(psz_var_domain
);
303 free(psz_decoded_path
);
308 if (stat(psz_uri
, &st
) == 0)
310 is_dir
= S_ISDIR(st
.st_mode
) != 0;
315 /* stat() fails with servers or shares. Assume directory. */
323 if (!vlc_credential_get(&credential
, access
, "smb-user",
324 "smb-pwd", SMB_LOGIN_DIALOG_TITLE
,
325 SMB_LOGIN_DIALOG_TEXT
, url
.psz_host
))
329 vlc_credential_store(&credential
, access
);
330 vlc_credential_clean(&credential
);
331 free(psz_var_domain
);
332 free(psz_decoded_path
);
335 access_sys_t
*sys
= vlc_obj_calloc(obj
, 1, sizeof (*sys
));
336 if (unlikely(sys
== NULL
))
348 access
->pf_readdir
= DirRead
;
349 access
->pf_control
= access_vaDirectoryControlHelper
;
354 access
->pf_read
= Read
;
355 access
->pf_control
= Control
;
356 access
->pf_seek
= Seek
;
357 fd
= open(psz_uri
, O_RDONLY
, 0);
368 static void Close(vlc_object_t
*obj
)
370 stream_t
*access
= (stream_t
*)obj
;
371 access_sys_t
*sys
= access
->p_sys
;
373 vlc_UrlClean(&sys
->url
);
379 set_description(N_("UNC input"))
380 set_help(N_("Microsoft Windows networking (UNC) input"))
381 set_capability("access", 0)
382 set_category(CAT_INPUT
)
383 set_subcategory(SUBCAT_INPUT_ACCESS
)
384 add_string("smb-user", NULL
, SMB_USER_TEXT
, SMB_USER_LONGTEXT
, false)
385 add_password("smb-pwd", NULL
, SMB_PASS_TEXT
, SMB_PASS_LONGTEXT
)
386 add_string("smb-domain", NULL
, SMB_DOMAIN_TEXT
, SMB_DOMAIN_LONGTEXT
, false)
388 set_callbacks(Open
, Close
)