smb.c 12.6 KB
Newer Older
1
2
3
/*****************************************************************************
 * smb.c: SMB input module
 *****************************************************************************
4
 * Copyright (C) 2001-2009 the VideoLAN team
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 * $Id$
 *
 * Authors: Gildas Bazin <gbazin@videolan.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
dionoea's avatar
dionoea committed
21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22
23
24
25
26
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
27
28
29
30
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

31
#include <vlc_common.h>
32
#include <vlc_fs.h>
33
#include <vlc_plugin.h>
zorglub's avatar
zorglub committed
34
#include <vlc_access.h>
35

36
#ifdef WIN32
ivoire's avatar
ivoire committed
37
38
39
#   ifdef HAVE_FCNTL_H
#       include <fcntl.h>
#   endif
40
41
42
43
#   ifdef HAVE_SYS_STAT_H
#       include <sys/stat.h>
#   endif
#   include <io.h>
44
#   define smbc_open(a,b,c) vlc_open(a,b,c)
45
46
47
48
49
50
51
#   define smbc_fstat(a,b) _fstati64(a,b)
#   define smbc_read read
#   define smbc_lseek _lseeki64
#   define smbc_close close
#else
#   include <libsmbclient.h>
#endif
52

53
54
#include <errno.h>

55
56
57
58
59
60
61
62
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
static int  Open ( vlc_object_t * );
static void Close( vlc_object_t * );

#define CACHING_TEXT N_("Caching value in ms")
#define CACHING_LONGTEXT N_( \
63
    "Caching value for SMB streams. This " \
zorglub's avatar
zorglub committed
64
    "value should be set in milliseconds." )
65
#define USER_TEXT N_("SMB user name")
zorglub's avatar
zorglub committed
66
#define USER_LONGTEXT N_("User name that will " \
67
68
    "be used for the connection.")
#define PASS_TEXT N_("SMB password")
zorglub's avatar
zorglub committed
69
#define PASS_LONGTEXT N_("Password that will be " \
70
71
    "used for the connection.")
#define DOMAIN_TEXT N_("SMB domain")
zorglub's avatar
zorglub committed
72
#define DOMAIN_LONGTEXT N_("Domain/Workgroup that " \
73
74
    "will be used for the connection.")

75
#define SMB_HELP N_("Samba (Windows network shares) input")
76
77
78
vlc_module_begin ()
    set_shortname( "SMB" )
    set_description( N_("SMB input") )
79
    set_help(SMB_HELP)
80
81
82
    set_capability( "access", 0 )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_ACCESS )
83
    add_integer( "smb-caching", 2 * DEFAULT_PTS_DELAY / 1000,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
84
                 CACHING_TEXT, CACHING_LONGTEXT, true )
85
        change_safe()
86
    add_string( "smb-user", NULL, USER_TEXT, USER_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
87
                false )
88
    add_password( "smb-pwd", NULL, PASS_TEXT,
89
                  PASS_LONGTEXT, false )
90
    add_string( "smb-domain", NULL, DOMAIN_TEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
91
                DOMAIN_LONGTEXT, false )
92
93
94
    add_shortcut( "smb" )
    set_callbacks( Open, Close )
vlc_module_end ()
95
96
97
98

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
99
static ssize_t Read( access_t *, uint8_t *, size_t );
100
static int Seek( access_t *, uint64_t );
101
102
103
104
105
106
107
static int Control( access_t *, int, va_list );

struct access_sys_t
{
    int i_smb;
};

108
109
#ifdef WIN32
static void Win32AddConnection( access_t *, char *, char *, char *, char * );
110
#else
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
111
112
static void smb_auth( const char *srv, const char *shr, char *wg, int wglen,
                      char *un, int unlen, char *pw, int pwlen )
113
{
Pierre's avatar
Pierre committed
114
115
116
117
118
119
120
121
    VLC_UNUSED(srv);
    VLC_UNUSED(shr);
    VLC_UNUSED(wg);
    VLC_UNUSED(wglen);
    VLC_UNUSED(un);
    VLC_UNUSED(unlen);
    VLC_UNUSED(pw);
    VLC_UNUSED(pwlen);
122
123
    //wglen = unlen = pwlen = 0;
}
124
#endif
125
126
127
128
129
130
131
132
133

/****************************************************************************
 * Open: connect to smb server and ask for file
 ****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys;
    struct stat  filestat;
Christophe Mutricy's avatar
Christophe Mutricy committed
134
    char         *psz_location, *psz_uri;
ivoire's avatar
ivoire committed
135
    char         *psz_user = NULL, *psz_pwd = NULL, *psz_domain = NULL;
136
137
138
    int          i_ret;
    int          i_smb;

139
140
    /* Parse input URI
     * [[[domain;]user[:password@]]server[/share[/path[/file]]]] */
Christophe Mutricy's avatar
Christophe Mutricy committed
141
142
    psz_location = strchr( p_access->psz_location, '/' );
    if( !psz_location )
143
    {
144
        msg_Err( p_access, "invalid SMB URI: smb://%s", psz_location );
145
146
147
148
        return VLC_EGENERIC;
    }
    else
    {
149
        char *psz_tmp = strdup( p_access->psz_location );
150
151
        char *psz_parser;

Christophe Mutricy's avatar
Christophe Mutricy committed
152
153
        psz_tmp[ psz_location - p_access->psz_location ] = 0;
        psz_location = p_access->psz_location;
154
155
156
157
158
        psz_parser = strchr( psz_tmp, '@' );
        if( psz_parser )
        {
            /* User info is there */
            *psz_parser = 0;
Christophe Mutricy's avatar
Christophe Mutricy committed
159
            psz_location = p_access->psz_location + (psz_parser - psz_tmp) + 1;
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183

            psz_parser = strchr( psz_tmp, ':' );
            if( psz_parser )
            {
                /* Password found */
                psz_pwd = strdup( psz_parser+1 );
                *psz_parser = 0;
            }

            psz_parser = strchr( psz_tmp, ';' );
            if( psz_parser )
            {
                /* Domain found */
                *psz_parser = 0; psz_parser++;
                psz_domain = strdup( psz_tmp );
            }
            else psz_parser = psz_tmp;

            psz_user = strdup( psz_parser );
        }

        free( psz_tmp );
    }

184
185
186
    /* Build an SMB URI
     * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] */

ivoire's avatar
ivoire committed
187
    if( !psz_user ) psz_user = var_InheritString( p_access, "smb-user" );
ivoire's avatar
ivoire committed
188
    if( psz_user && !*psz_user ) { free( psz_user ); psz_user = NULL; }
ivoire's avatar
ivoire committed
189
    if( !psz_pwd ) psz_pwd = var_InheritString( p_access, "smb-pwd" );
ivoire's avatar
ivoire committed
190
    if( psz_pwd && !*psz_pwd ) { free( psz_pwd ); psz_pwd = NULL; }
ivoire's avatar
ivoire committed
191
    if( !psz_domain ) psz_domain = var_InheritString( p_access, "smb-domain" );
ivoire's avatar
ivoire committed
192
    if( psz_domain && !*psz_domain ) { free( psz_domain ); psz_domain = NULL; }
193

194
195
#ifdef WIN32
    if( psz_user )
Christophe Mutricy's avatar
Christophe Mutricy committed
196
197
        Win32AddConnection( p_access, psz_location, psz_user, psz_pwd, psz_domain);
    i_ret = asprintf( &psz_uri, "//%s", psz_location );
198
#else
199
    if( psz_user )
ivoire's avatar
ivoire committed
200
201
202
        i_ret = asprintf( &psz_uri, "smb://%s%s%s%s%s@%s",
                          psz_domain ? psz_domain : "", psz_domain ? ";" : "",
                          psz_user, psz_pwd ? ":" : "",
Christophe Mutricy's avatar
Christophe Mutricy committed
203
                          psz_pwd ? psz_pwd : "", psz_location );
204
    else
Christophe Mutricy's avatar
Christophe Mutricy committed
205
        i_ret = asprintf( &psz_uri, "smb://%s", psz_location );
206
#endif
207

208
209
210
    free( psz_user );
    free( psz_pwd );
    free( psz_domain );
211

ivoire's avatar
ivoire committed
212
213
214
    if( i_ret == -1 )
        return VLC_ENOMEM;

215
#ifndef WIN32
216
    if( smbc_init( smb_auth, 0 ) )
217
218
219
220
    {
        free( psz_uri );
        return VLC_EGENERIC;
    }
221
#endif
222

223
224
225
226
227
228
229
230
/*
** some version of glibc defines open as a macro, causing havoc
** with other macros using 'open' under the hood, such as the
** following one:
*/
#if defined(smbc_open) && defined(open)
# undef open
#endif
231
    if( (i_smb = smbc_open( psz_uri, O_RDONLY, 0 )) < 0 )
232
    {
233
234
        msg_Err( p_access, "open failed for '%s' (%m)",
                 p_access->psz_location );
235
236
237
238
        free( psz_uri );
        return VLC_EGENERIC;
    }

239
240
241
    /* Init p_access */
    STANDARD_READ_ACCESS_INIT;

242
    i_ret = smbc_fstat( i_smb, &filestat );
243
244
245
246
247
    if( i_ret )
    {
        errno = i_ret;
        msg_Err( p_access, "stat failed (%m)" );
    }
248
249
    else
        p_access->info.i_size = filestat.st_size;
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275

    free( psz_uri );

    p_sys->i_smb = i_smb;

    /* Update default_pts to a suitable value for smb access */
    var_Create( p_access, "smb-caching", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Close: free unused data structures
 *****************************************************************************/
static void Close( vlc_object_t *p_this )
{
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys = p_access->p_sys;

    smbc_close( p_sys->i_smb );
    free( p_sys );
}

/*****************************************************************************
 * Seek: try to go at the right place
 *****************************************************************************/
276
static int Seek( access_t *p_access, uint64_t i_pos )
277
278
279
280
{
    access_sys_t *p_sys = p_access->p_sys;
    int64_t      i_ret;

281
282
    if( i_pos >= INT64_MAX )
        return VLC_EGENERIC;
283

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
284
    msg_Dbg( p_access, "seeking to %"PRId64, i_pos );
285
286
287
288

    i_ret = smbc_lseek( p_sys->i_smb, i_pos, SEEK_SET );
    if( i_ret == -1 )
    {
289
        msg_Err( p_access, "seek failed (%m)" );
290
291
292
        return VLC_EGENERIC;
    }

293
    p_access->info.b_eof = false;
294
295
296
297
298
299
300
301
    p_access->info.i_pos = i_ret;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Read:
 *****************************************************************************/
302
static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
303
304
305
306
307
308
309
310
311
{
    access_sys_t *p_sys = p_access->p_sys;
    int i_read;

    if( p_access->info.b_eof ) return 0;

    i_read = smbc_read( p_sys->i_smb, p_buffer, i_len );
    if( i_read < 0 )
    {
312
        msg_Err( p_access, "read failed (%m)" );
313
314
315
        return -1;
    }

316
    if( i_read == 0 ) p_access->info.b_eof = true;
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
    else if( i_read > 0 ) p_access->info.i_pos += i_read;

    return i_read;
}

/*****************************************************************************
 * Control:
 *****************************************************************************/
static int Control( access_t *p_access, int i_query, va_list args )
{
    switch( i_query )
    {
    case ACCESS_CAN_SEEK:
    case ACCESS_CAN_FASTSEEK:
    case ACCESS_CAN_PAUSE:
    case ACCESS_CAN_CONTROL_PACE:
333
        *va_arg( args, bool* ) = true;
334
335
336
        break;

    case ACCESS_GET_PTS_DELAY:
337
        *va_arg( args, int64_t * )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
338
                  = var_GetInteger( p_access, "smb-caching" ) * 1000;
339
340
341
342
343
344
345
346
347
348
        break;

    case ACCESS_SET_PAUSE_STATE:
        /* Nothing to do */
        break;

    case ACCESS_GET_TITLE_INFO:
    case ACCESS_SET_TITLE:
    case ACCESS_SET_SEEKPOINT:
    case ACCESS_SET_PRIVATE_ID_STATE:
349
    case ACCESS_GET_CONTENT_TYPE:
350
351
352
353
354
355
356
357
358
359
        return VLC_EGENERIC;

    default:
        msg_Warn( p_access, "unimplemented query in control" );
        return VLC_EGENERIC;

    }

    return VLC_SUCCESS;
}
360
361
362
363
364
365

#ifdef WIN32
static void Win32AddConnection( access_t *p_access, char *psz_path,
                                char *psz_user, char *psz_pwd,
                                char *psz_domain )
{
Rafaël Carré's avatar
Rafaël Carré committed
366
    DWORD WINAPI (*OurWNetAddConnection2)( LPNETRESOURCE, LPCTSTR, LPCTSTR, DWORD );
367
368
369
    char psz_remote[MAX_PATH], psz_server[MAX_PATH], psz_share[MAX_PATH];
    NETRESOURCE net_resource;
    DWORD i_result;
370
    char *psz_parser;
371
    VLC_UNUSED( psz_domain );
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391

    HINSTANCE hdll = LoadLibrary(_T("MPR.DLL"));
    if( !hdll )
    {
        msg_Warn( p_access, "couldn't load mpr.dll" );
        return;
    }

    OurWNetAddConnection2 =
      (void *)GetProcAddress( hdll, _T("WNetAddConnection2A") );
    if( !OurWNetAddConnection2 )
    {
        msg_Warn( p_access, "couldn't find WNetAddConnection2 in mpr.dll" );
        return;
    }

    memset( &net_resource, 0, sizeof(net_resource) );
    net_resource.dwType = RESOURCETYPE_DISK;

    /* Find out server and share names */
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
392
    strlcpy( psz_server, psz_path, sizeof( psz_server ) );
393
    psz_share[0] = 0;
394
395
396
    psz_parser = strchr( psz_path, '/' );
    if( psz_parser )
    {
397
        char *psz_parser2 = strchr( ++psz_parser, '/' );
398
        if( psz_parser2 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
399
            strlcpy( psz_share, psz_parser, sizeof( psz_share ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
400
   }
401

402
    snprintf( psz_remote, sizeof( psz_remote ), "\\\\%s\\%s", psz_server, psz_share );
403
404
405
406
407
408
409
410
    net_resource.lpRemoteName = psz_remote;

    i_result = OurWNetAddConnection2( &net_resource, psz_pwd, psz_user, 0 );

    if( i_result != NO_ERROR )
    {
        msg_Dbg( p_access, "connected to %s", psz_remote );
    }
411
    else if( i_result != ERROR_ALREADY_ASSIGNED &&
412
413
414
415
416
417
418
419
420
421
422
423
             i_result != ERROR_DEVICE_ALREADY_REMEMBERED )
    {
        msg_Dbg( p_access, "already connected to %s", psz_remote );
    }
    else
    {
        msg_Dbg( p_access, "failed to connect to %s", psz_remote );
    }

    FreeLibrary( hdll );
}
#endif // WIN32