1 /*****************************************************************************
2 * smb.c: SMB input module
3 *****************************************************************************
4 * Copyright (C) 2001-2004 the VideoLAN team
7 * Authors: Gildas Bazin <gbazin@videolan.org>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 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 General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_access.h>
41 # ifdef HAVE_SYS_STAT_H
42 # include <sys/stat.h>
45 # define smbc_open(a,b,c) open(a,b,c)
46 # define stat _stati64
47 # define smbc_fstat(a,b) _fstati64(a,b)
48 # define smbc_read read
49 # define smbc_lseek _lseeki64
50 # define smbc_close close
52 # include <libsmbclient.h>
58 /*****************************************************************************
60 *****************************************************************************/
61 static int Open ( vlc_object_t
* );
62 static void Close( vlc_object_t
* );
64 #define CACHING_TEXT N_("Caching value in ms")
65 #define CACHING_LONGTEXT N_( \
66 "Caching value for SMB streams. This " \
67 "value should be set in milliseconds." )
68 #define USER_TEXT N_("SMB user name")
69 #define USER_LONGTEXT N_("User name that will " \
70 "be used for the connection.")
71 #define PASS_TEXT N_("SMB password")
72 #define PASS_LONGTEXT N_("Password that will be " \
73 "used for the connection.")
74 #define DOMAIN_TEXT N_("SMB domain")
75 #define DOMAIN_LONGTEXT N_("Domain/Workgroup that " \
76 "will be used for the connection.")
79 set_shortname( "SMB" );
80 set_description( N_("SMB input") );
81 set_capability( "access", 0 );
82 set_category( CAT_INPUT
);
83 set_subcategory( SUBCAT_INPUT_ACCESS
);
84 add_integer( "smb-caching", 2 * DEFAULT_PTS_DELAY
/ 1000, NULL
,
85 CACHING_TEXT
, CACHING_LONGTEXT
, true );
86 add_string( "smb-user", NULL
, NULL
, USER_TEXT
, USER_LONGTEXT
,
88 add_string( "smb-pwd", NULL
, NULL
, PASS_TEXT
,
89 PASS_LONGTEXT
, false );
90 add_string( "smb-domain", NULL
, NULL
, DOMAIN_TEXT
,
91 DOMAIN_LONGTEXT
, false );
92 add_shortcut( "smb" );
93 set_callbacks( Open
, Close
);
96 /*****************************************************************************
98 *****************************************************************************/
99 static ssize_t
Read( access_t
*, uint8_t *, size_t );
100 static int Seek( access_t
*, int64_t );
101 static int Control( access_t
*, int, va_list );
114 static void Win32AddConnection( access_t
*, char *, char *, char *, char * );
116 static void smb_auth( const char *srv
, const char *shr
, char *wg
, int wglen
,
117 char *un
, int unlen
, char *pw
, int pwlen
)
119 //wglen = unlen = pwlen = 0;
123 /****************************************************************************
124 * Open: connect to smb server and ask for file
125 ****************************************************************************/
126 static int Open( vlc_object_t
*p_this
)
128 access_t
*p_access
= (access_t
*)p_this
;
130 struct stat filestat
;
131 char *psz_path
, *psz_uri
;
132 char *psz_user
= NULL
, *psz_pwd
= NULL
, *psz_domain
= NULL
;
143 * [[[domain;]user[:password@]]server[/share[/path[/file]]]] */
145 psz_path
= strchr( p_access
->psz_path
, '/' );
148 msg_Err( p_access
, "invalid SMB URI: smb://%s", psz_path
);
153 char *psz_tmp
= strdup( p_access
->psz_path
);
156 psz_tmp
[ psz_path
- p_access
->psz_path
] = 0;
157 psz_path
= p_access
->psz_path
;
158 psz_parser
= strchr( psz_tmp
, '@' );
161 /* User info is there */
163 psz_path
= p_access
->psz_path
+ (psz_parser
- psz_tmp
) + 1;
165 psz_parser
= strchr( psz_tmp
, ':' );
169 psz_pwd
= strdup( psz_parser
+1 );
173 psz_parser
= strchr( psz_tmp
, ';' );
177 *psz_parser
= 0; psz_parser
++;
178 psz_domain
= strdup( psz_tmp
);
180 else psz_parser
= psz_tmp
;
182 psz_user
= strdup( psz_parser
);
189 * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] */
191 if( !psz_user
) psz_user
= var_CreateGetString( p_access
, "smb-user" );
192 if( psz_user
&& !*psz_user
) { free( psz_user
); psz_user
= NULL
; }
193 if( !psz_pwd
) psz_pwd
= var_CreateGetString( p_access
, "smb-pwd" );
194 if( psz_pwd
&& !*psz_pwd
) { free( psz_pwd
); psz_pwd
= NULL
; }
195 if( !psz_domain
) psz_domain
= var_CreateGetString( p_access
, "smb-domain" );
196 if( psz_domain
&& !*psz_domain
) { free( psz_domain
); psz_domain
= NULL
; }
200 Win32AddConnection( p_access
, psz_path
, psz_user
, psz_pwd
, psz_domain
);
201 i_ret
= asprintf( &psz_uri
, "//%s", psz_path
);
204 i_ret
= asprintf( &psz_uri
, "smb://%s%s%s%s%s@%s",
205 psz_domain
? psz_domain
: "", psz_domain
? ";" : "",
206 psz_user
, psz_pwd
? ":" : "",
207 psz_pwd
? psz_pwd
: "", psz_path
);
209 i_ret
= asprintf( &psz_uri
, "smb://%s", psz_path
);
220 if( !(p_smb
= smbc_new_context()) )
226 p_smb
->callbacks
.auth_fn
= smb_auth
;
228 if( !smbc_init_context( p_smb
) )
230 msg_Err( p_access
, "cannot initialize context (%m)" );
231 smbc_free_context( p_smb
, 1 );
236 if( !(p_file
= (p_smb
->open
)( p_smb
, psz_uri
, O_RDONLY
, 0 )) )
238 msg_Err( p_access
, "open failed for '%s' (%m)",
239 p_access
->psz_path
);
240 smbc_free_context( p_smb
, 1 );
246 STANDARD_READ_ACCESS_INIT
;
248 i_ret
= p_smb
->fstat( p_smb
, p_file
, &filestat
);
249 if( i_ret
) msg_Err( p_access
, "stat failed (%m)" );
250 else p_access
->info
.i_size
= filestat
.st_size
;
254 if( smbc_init( smb_auth
, 1 ) )
262 ** some version of glibc defines open as a macro, causing havoc
263 ** with other macros using 'open' under the hood, such as the
266 #if defined(smbc_open) && defined(open)
269 if( (i_smb
= smbc_open( psz_uri
, O_RDONLY
, 0 )) < 0 )
271 msg_Err( p_access
, "open failed for '%s' (%m)",
272 p_access
->psz_path
);
278 STANDARD_READ_ACCESS_INIT
;
280 i_ret
= smbc_fstat( i_smb
, &filestat
);
284 msg_Err( p_access
, "stat failed (%m)" );
286 else p_access
->info
.i_size
= filestat
.st_size
;
292 p_sys
->p_smb
= p_smb
;
293 p_sys
->p_file
= p_file
;
295 p_sys
->i_smb
= i_smb
;
298 /* Update default_pts to a suitable value for smb access */
299 var_Create( p_access
, "smb-caching", VLC_VAR_INTEGER
| VLC_VAR_DOINHERIT
);
304 /*****************************************************************************
305 * Close: free unused data structures
306 *****************************************************************************/
307 static void Close( vlc_object_t
*p_this
)
309 access_t
*p_access
= (access_t
*)p_this
;
310 access_sys_t
*p_sys
= p_access
->p_sys
;
313 # ifndef HAVE__SMBCCTX_CLOSE_FN
314 p_sys
->p_smb
->close( p_sys
->p_smb
, p_sys
->p_file
);
316 p_sys
->p_smb
->close_fn( p_sys
->p_smb
, p_sys
->p_file
);
318 smbc_free_context( p_sys
->p_smb
, 1 );
320 smbc_close( p_sys
->i_smb
);
326 /*****************************************************************************
327 * Seek: try to go at the right place
328 *****************************************************************************/
329 static int Seek( access_t
*p_access
, int64_t i_pos
)
331 access_sys_t
*p_sys
= p_access
->p_sys
;
334 if( i_pos
< 0 ) return VLC_EGENERIC
;
336 msg_Dbg( p_access
, "seeking to %"PRId64
, i_pos
);
339 i_ret
= p_sys
->p_smb
->lseek(p_sys
->p_smb
, p_sys
->p_file
, i_pos
, SEEK_SET
);
341 i_ret
= smbc_lseek( p_sys
->i_smb
, i_pos
, SEEK_SET
);
345 msg_Err( p_access
, "seek failed (%m)" );
349 p_access
->info
.b_eof
= false;
350 p_access
->info
.i_pos
= i_ret
;
355 /*****************************************************************************
357 *****************************************************************************/
358 static ssize_t
Read( access_t
*p_access
, uint8_t *p_buffer
, size_t i_len
)
360 access_sys_t
*p_sys
= p_access
->p_sys
;
363 if( p_access
->info
.b_eof
) return 0;
366 i_read
= p_sys
->p_smb
->read(p_sys
->p_smb
, p_sys
->p_file
, p_buffer
, i_len
);
368 i_read
= smbc_read( p_sys
->i_smb
, p_buffer
, i_len
);
372 msg_Err( p_access
, "read failed (%m)" );
376 if( i_read
== 0 ) p_access
->info
.b_eof
= true;
377 else if( i_read
> 0 ) p_access
->info
.i_pos
+= i_read
;
382 /*****************************************************************************
384 *****************************************************************************/
385 static int Control( access_t
*p_access
, int i_query
, va_list args
)
393 case ACCESS_CAN_SEEK
:
394 pb_bool
= (bool*)va_arg( args
, bool* );
397 case ACCESS_CAN_FASTSEEK
:
398 pb_bool
= (bool*)va_arg( args
, bool* );
401 case ACCESS_CAN_PAUSE
:
402 pb_bool
= (bool*)va_arg( args
, bool* );
405 case ACCESS_CAN_CONTROL_PACE
:
406 pb_bool
= (bool*)va_arg( args
, bool* );
411 pi_int
= (int*)va_arg( args
, int * );
415 case ACCESS_GET_PTS_DELAY
:
416 pi_64
= (int64_t*)va_arg( args
, int64_t * );
417 *pi_64
= (int64_t)var_GetInteger( p_access
, "smb-caching" ) * 1000;
420 case ACCESS_SET_PAUSE_STATE
:
424 case ACCESS_GET_TITLE_INFO
:
425 case ACCESS_SET_TITLE
:
426 case ACCESS_SET_SEEKPOINT
:
427 case ACCESS_SET_PRIVATE_ID_STATE
:
428 case ACCESS_GET_CONTENT_TYPE
:
432 msg_Warn( p_access
, "unimplemented query in control" );
441 static void Win32AddConnection( access_t
*p_access
, char *psz_path
,
442 char *psz_user
, char *psz_pwd
,
445 DWORD (*OurWNetAddConnection2
)( LPNETRESOURCE
, LPCTSTR
, LPCTSTR
, DWORD
);
446 char psz_remote
[MAX_PATH
], psz_server
[MAX_PATH
], psz_share
[MAX_PATH
];
447 NETRESOURCE net_resource
;
450 VLC_UNUSED( psz_domain
);
452 HINSTANCE hdll
= LoadLibrary(_T("MPR.DLL"));
455 msg_Warn( p_access
, "couldn't load mpr.dll" );
459 OurWNetAddConnection2
=
460 (void *)GetProcAddress( hdll
, _T("WNetAddConnection2A") );
461 if( !OurWNetAddConnection2
)
463 msg_Warn( p_access
, "couldn't find WNetAddConnection2 in mpr.dll" );
467 memset( &net_resource
, 0, sizeof(net_resource
) );
468 net_resource
.dwType
= RESOURCETYPE_DISK
;
470 /* Find out server and share names */
471 strlcpy( psz_server
, psz_path
, sizeof( psz_server
) );
473 psz_parser
= strchr( psz_path
, '/' );
476 char *psz_parser2
= strchr( ++psz_parser
, '/' );
478 strlcpy( psz_share
, psz_parser
, sizeof( psz_share
) );
481 sprintf( psz_remote
, "\\\\%s\\%s", psz_server
, psz_share
);
482 net_resource
.lpRemoteName
= psz_remote
;
484 i_result
= OurWNetAddConnection2( &net_resource
, psz_pwd
, psz_user
, 0 );
486 if( i_result
!= NO_ERROR
)
488 msg_Dbg( p_access
, "connected to %s", psz_remote
);
490 else if( i_result
!= ERROR_ALREADY_ASSIGNED
&&
491 i_result
!= ERROR_DEVICE_ALREADY_REMEMBERED
)
493 msg_Dbg( p_access
, "already connected to %s", psz_remote
);
497 msg_Dbg( p_access
, "failed to connect to %s", psz_remote
);