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
,
55 const char *pwd
, const char *domain
)
57 NETRESOURCE net_resource
;
58 char remote_name
[MAX_PATH
];
62 memset(&net_resource
, 0, sizeof (net_resource
));
63 net_resource
.dwType
= RESOURCETYPE_DISK
;
65 snprintf(remote_name
, sizeof (remote_name
), "\\\\%s\\%s", server
,
66 (share
!= NULL
) ? share
+ 1 /* skip leading '/' */: "");
68 /* remove trailings '/' */
69 char *delim
= strchr(remote_name
, '/');
74 net_resource
.lpRemoteName
= remote_name
;
76 switch (WNetAddConnection2(&net_resource
, pwd
, user
, 0))
79 msg
= "connected to %s";
81 case ERROR_ALREADY_ASSIGNED
:
82 case ERROR_DEVICE_ALREADY_REMEMBERED
:
83 msg
= "already connected to %s";
86 msg
= "failed to connect to %s";
88 msg_Dbg(access
, msg
, remote_name
);
92 * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] */
93 static int smb_get_uri( stream_t
*p_access
, char **ppsz_uri
,
94 const char *psz_domain
,
95 const char *psz_user
, const char *psz_pwd
,
96 const char *psz_server
, const char *psz_share_path
,
97 const char *psz_name
)
100 #define PSZ_SHARE_PATH_OR_NULL psz_share_path ? psz_share_path : ""
101 #define PSZ_NAME_OR_NULL psz_name ? "/" : "", psz_name ? psz_name : ""
103 Win32AddConnection( p_access
, psz_server
, psz_share_path
,
104 psz_user
, psz_pwd
, psz_domain
);
105 return asprintf( ppsz_uri
, "//%s%s%s%s", psz_server
, PSZ_SHARE_PATH_OR_NULL
,
109 static int Seek( stream_t
*p_access
, uint64_t i_pos
)
111 access_sys_t
*p_sys
= p_access
->p_sys
;
114 if( i_pos
>= INT64_MAX
)
117 msg_Dbg( p_access
, "seeking to %"PRId64
, i_pos
);
119 i_ret
= lseek( p_sys
->i_smb
, i_pos
, SEEK_SET
);
122 msg_Err( p_access
, "seek failed (%s)", vlc_strerror_c(errno
) );
129 static ssize_t
Read( stream_t
*p_access
, void *p_buffer
, size_t i_len
)
131 access_sys_t
*p_sys
= p_access
->p_sys
;
134 i_read
= read( p_sys
->i_smb
, p_buffer
, i_len
);
137 msg_Err( p_access
, "read failed (%s)", vlc_strerror_c(errno
) );
144 static int DirRead(stream_t
*p_access
, input_item_node_t
*p_node
)
146 access_sys_t
*p_sys
= p_access
->p_sys
;
147 int i_ret
= VLC_SUCCESS
;
149 struct vlc_readdir_helper rdh
;
150 vlc_readdir_helper_init( &rdh
, p_access
, p_node
);
152 // Handle share listing from here. Directory browsing is handled by the
153 // usual filesystem module.
154 SHARE_INFO_1
*p_info
;
155 DWORD i_share_enum_res
;
157 DWORD i_resume_handle
= 0;
158 DWORD i_total_elements
; // Unused, but needs to be passed
159 wchar_t *wpsz_host
= ToWide( p_sys
->url
.psz_host
);
160 if( wpsz_host
== NULL
)
164 i_share_enum_res
= NetShareEnum( wpsz_host
, 1, (LPBYTE
*)&p_info
,
165 MAX_PREFERRED_LENGTH
, &i_nb_elem
,
166 &i_total_elements
, &i_resume_handle
);
167 if( i_share_enum_res
== ERROR_SUCCESS
||
168 i_share_enum_res
== ERROR_MORE_DATA
)
170 for ( DWORD i
= 0; i
< i_nb_elem
; ++i
)
172 SHARE_INFO_1
*p_current
= p_info
+ i
;
173 if( p_current
->shi1_type
& STYPE_SPECIAL
)
175 char* psz_name
= FromWide( p_current
->shi1_netname
);
176 if( psz_name
== NULL
)
183 if( smb_get_uri( p_access
, &psz_path
, NULL
, NULL
, NULL
,
184 p_sys
->url
.psz_host
, p_sys
->url
.psz_path
,
191 // We need to concatenate the scheme before, as the window version
192 // of smb_get_uri generates a path (and the other call site needs
193 // a path). The path is already prefixed by "//" so we just need
196 if( asprintf( &psz_uri
, "file:%s", psz_path
) < 0 )
205 i_ret
= vlc_readdir_helper_additem( &rdh
, psz_uri
, NULL
,
206 psz_name
, ITEM_TYPE_DIRECTORY
, ITEM_NET
);
211 NetApiBufferFree( p_info
);
212 } while( i_share_enum_res
== ERROR_MORE_DATA
&& i_ret
== VLC_SUCCESS
);
216 vlc_readdir_helper_finish( &rdh
, i_ret
== VLC_SUCCESS
);
221 static int Control( stream_t
*p_access
, int i_query
, va_list args
)
223 access_sys_t
*sys
= p_access
->p_sys
;
227 case STREAM_CAN_SEEK
:
228 case STREAM_CAN_PAUSE
:
229 case STREAM_CAN_CONTROL_PACE
:
230 *va_arg( args
, bool* ) = true;
233 case STREAM_CAN_FASTSEEK
:
234 *va_arg( args
, bool* ) = false;
237 case STREAM_GET_SIZE
:
238 if( p_access
->pf_readdir
!= NULL
)
240 *va_arg( args
, uint64_t * ) = sys
->size
;
243 case STREAM_GET_PTS_DELAY
:
244 *va_arg( args
, vlc_tick_t
* ) = VLC_TICK_FROM_MS(
245 var_InheritInteger( p_access
, "network-caching" ) );
248 case STREAM_SET_PAUSE_STATE
:
259 static int Open(vlc_object_t
*obj
)
261 stream_t
*access
= (stream_t
*)obj
;
263 vlc_credential credential
;
264 char *psz_decoded_path
= NULL
, *psz_uri
= NULL
, *psz_var_domain
= NULL
;
269 if (vlc_UrlParseFixup(&url
, access
->psz_url
) != 0)
275 if (url
.psz_path
!= NULL
)
277 psz_decoded_path
= vlc_uri_decode_duplicate(url
.psz_path
);
278 if (psz_decoded_path
== NULL
)
285 vlc_credential_init(&credential
, &url
);
286 psz_var_domain
= var_InheritString(access
, "smb-domain");
287 credential
.psz_realm
= psz_var_domain
;
288 vlc_credential_get(&credential
, access
, "smb-user", "smb-pwd", NULL
, NULL
);
294 if (smb_get_uri(access
, &psz_uri
, credential
.psz_realm
,
295 credential
.psz_username
, credential
.psz_password
,
296 url
.psz_host
, psz_decoded_path
, NULL
) == -1 )
298 vlc_credential_clean(&credential
);
299 free(psz_var_domain
);
300 free(psz_decoded_path
);
305 if (stat(psz_uri
, &st
) == 0)
307 is_dir
= S_ISDIR(st
.st_mode
) != 0;
312 /* stat() fails with servers or shares. Assume directory. */
320 if (!vlc_credential_get(&credential
, access
, "smb-user",
321 "smb-pwd", SMB_LOGIN_DIALOG_TITLE
,
322 SMB_LOGIN_DIALOG_TEXT
, url
.psz_host
))
326 vlc_credential_store(&credential
, access
);
327 vlc_credential_clean(&credential
);
328 free(psz_var_domain
);
329 free(psz_decoded_path
);
332 access_sys_t
*sys
= vlc_obj_calloc(obj
, 1, sizeof (*sys
));
333 if (unlikely(sys
== NULL
))
345 access
->pf_readdir
= DirRead
;
346 access
->pf_control
= access_vaDirectoryControlHelper
;
350 access
->pf_read
= Read
;
351 access
->pf_control
= Control
;
352 access
->pf_seek
= Seek
;
353 fd
= open(psz_uri
, O_RDONLY
, 0);
364 static void Close(vlc_object_t
*obj
)
366 stream_t
*access
= (stream_t
*)obj
;
367 access_sys_t
*sys
= access
->p_sys
;
369 vlc_UrlClean(&sys
->url
);
375 set_description(N_("UNC input"))
376 set_help(N_("Microsoft Windows networking (UNC) input"))
377 set_capability("access", 0)
378 set_category(CAT_INPUT
)
379 set_subcategory(SUBCAT_INPUT_ACCESS
)
380 add_string("smb-user", NULL
, SMB_USER_TEXT
, SMB_USER_LONGTEXT
, false)
381 add_password("smb-pwd", NULL
, SMB_PASS_TEXT
, SMB_PASS_LONGTEXT
)
382 add_string("smb-domain", NULL
, SMB_DOMAIN_TEXT
, SMB_DOMAIN_LONGTEXT
, false)
384 set_callbacks(Open
, Close
)