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
 *****************************************************************************/
dionoea's avatar
dionoea committed
27

28

29
30
31
32
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

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

37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#ifdef WIN32
#ifdef HAVE_FCNTL_H
#   include <fcntl.h>
#endif
#   ifdef HAVE_SYS_STAT_H
#       include <sys/stat.h>
#   endif
#   include <io.h>
#   define smbc_open(a,b,c) open(a,b,c)
#   define stat _stati64
#   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
54

55
56
#include <errno.h>

57
58
59
60
61
62
63
64
/*****************************************************************************
 * 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_( \
65
    "Caching value for SMB streams. This " \
zorglub's avatar
zorglub committed
66
    "value should be set in milliseconds." )
67
#define USER_TEXT N_("SMB user name")
zorglub's avatar
zorglub committed
68
#define USER_LONGTEXT N_("User name that will " \
69
70
    "be used for the connection.")
#define PASS_TEXT N_("SMB password")
zorglub's avatar
zorglub committed
71
#define PASS_LONGTEXT N_("Password that will be " \
72
73
    "used for the connection.")
#define DOMAIN_TEXT N_("SMB domain")
zorglub's avatar
zorglub committed
74
#define DOMAIN_LONGTEXT N_("Domain/Workgroup that " \
75
76
    "will be used for the connection.")

77
78
79
80
81
82
vlc_module_begin ()
    set_shortname( "SMB" )
    set_description( N_("SMB input") )
    set_capability( "access", 0 )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_ACCESS )
83
    add_integer( "smb-caching", 2 * DEFAULT_PTS_DELAY / 1000, NULL,
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, NULL, USER_TEXT, USER_LONGTEXT,
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
87
                false )
88
89
    add_password( "smb-pwd", NULL, NULL, PASS_TEXT,
                  PASS_LONGTEXT, false )
90
    add_string( "smb-domain", NULL, 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
101
102
103
104
105
106
107
static int Seek( access_t *, int64_t );
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
114
115
{
    //wglen = unlen = pwlen = 0;
}
116
#endif
117
118
119
120
121
122
123
124
125

/****************************************************************************
 * 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;
126
    char         *psz_path, *psz_uri;
ivoire's avatar
ivoire committed
127
    char         *psz_user = NULL, *psz_pwd = NULL, *psz_domain = NULL;
128
129
130
    int          i_ret;
    int          i_smb;

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
    /* Parse input URI
     * [[[domain;]user[:password@]]server[/share[/path[/file]]]] */
    psz_path = strchr( p_access->psz_path, '/' );
    if( !psz_path )
    {
        msg_Err( p_access, "invalid SMB URI: smb://%s", psz_path );
        return VLC_EGENERIC;
    }
    else
    {
        char *psz_tmp = strdup( p_access->psz_path );
        char *psz_parser;

        psz_tmp[ psz_path - p_access->psz_path ] = 0;
        psz_path = p_access->psz_path;
        psz_parser = strchr( psz_tmp, '@' );
        if( psz_parser )
        {
            /* User info is there */
            *psz_parser = 0;
            psz_path = p_access->psz_path + (psz_parser - psz_tmp) + 1;

            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 );
    }

176
177
178
    /* Build an SMB URI
     * smb://[[[domain;]user[:password@]]server[/share[/path[/file]]]] */

179
    if( !psz_user ) psz_user = var_CreateGetString( p_access, "smb-user" );
ivoire's avatar
ivoire committed
180
    if( psz_user && !*psz_user ) { free( psz_user ); psz_user = NULL; }
181
    if( !psz_pwd ) psz_pwd = var_CreateGetString( p_access, "smb-pwd" );
ivoire's avatar
ivoire committed
182
    if( psz_pwd && !*psz_pwd ) { free( psz_pwd ); psz_pwd = NULL; }
183
    if( !psz_domain ) psz_domain = var_CreateGetString( p_access, "smb-domain" );
ivoire's avatar
ivoire committed
184
    if( psz_domain && !*psz_domain ) { free( psz_domain ); psz_domain = NULL; }
185

186
187
188
#ifdef WIN32
    if( psz_user )
        Win32AddConnection( p_access, psz_path, psz_user, psz_pwd, psz_domain);
ivoire's avatar
ivoire committed
189
    i_ret = asprintf( &psz_uri, "//%s", psz_path );
190
#else
191
    if( psz_user )
ivoire's avatar
ivoire committed
192
193
194
195
        i_ret = asprintf( &psz_uri, "smb://%s%s%s%s%s@%s",
                          psz_domain ? psz_domain : "", psz_domain ? ";" : "",
                          psz_user, psz_pwd ? ":" : "",
                          psz_pwd ? psz_pwd : "", psz_path );
196
    else
ivoire's avatar
ivoire committed
197
        i_ret = asprintf( &psz_uri, "smb://%s", psz_path );
198
#endif
199

200
201
202
    free( psz_user );
    free( psz_pwd );
    free( psz_domain );
203

ivoire's avatar
ivoire committed
204
205
206
    if( i_ret == -1 )
        return VLC_ENOMEM;

207
#ifndef WIN32
208
    if( smbc_init( smb_auth, 0 ) )
209
210
211
212
    {
        free( psz_uri );
        return VLC_EGENERIC;
    }
213
#endif
214

215
216
217
218
219
220
221
222
/*
** 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
223
    if( (i_smb = smbc_open( psz_uri, O_RDONLY, 0 )) < 0 )
224
    {
225
        msg_Err( p_access, "open failed for '%s' (%m)", p_access->psz_path );
226
227
228
229
        free( psz_uri );
        return VLC_EGENERIC;
    }

230
231
232
    /* Init p_access */
    STANDARD_READ_ACCESS_INIT;

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

    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
 *****************************************************************************/
static int Seek( access_t *p_access, int64_t i_pos )
{
    access_sys_t *p_sys = p_access->p_sys;
    int64_t      i_ret;

    if( i_pos < 0 ) return VLC_EGENERIC;

Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
274
    msg_Dbg( p_access, "seeking to %"PRId64, i_pos );
275
276
277
278

    i_ret = smbc_lseek( p_sys->i_smb, i_pos, SEEK_SET );
    if( i_ret == -1 )
    {
279
        msg_Err( p_access, "seek failed (%m)" );
280
281
282
        return VLC_EGENERIC;
    }

283
    p_access->info.b_eof = false;
284
285
286
287
288
289
290
291
    p_access->info.i_pos = i_ret;

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Read:
 *****************************************************************************/
292
static ssize_t Read( access_t *p_access, uint8_t *p_buffer, size_t i_len )
293
294
295
296
297
298
299
300
301
{
    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 )
    {
302
        msg_Err( p_access, "read failed (%m)" );
303
304
305
        return -1;
    }

306
    if( i_read == 0 ) p_access->info.b_eof = true;
307
308
309
310
311
312
313
314
315
316
    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 )
{
ivoire's avatar
ivoire committed
317
318
    bool        *pb_bool;
    int64_t     *pi_64;
319
320
321
322

    switch( i_query )
    {
    case ACCESS_CAN_SEEK:
323
324
        pb_bool = (bool*)va_arg( args, bool* );
        *pb_bool = true;
325
326
        break;
    case ACCESS_CAN_FASTSEEK:
327
328
        pb_bool = (bool*)va_arg( args, bool* );
        *pb_bool = true;
329
330
        break;
    case ACCESS_CAN_PAUSE:
331
332
        pb_bool = (bool*)va_arg( args, bool* );
        *pb_bool = true;
333
334
        break;
    case ACCESS_CAN_CONTROL_PACE:
335
336
        pb_bool = (bool*)va_arg( args, bool* );
        *pb_bool = true;
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
        break;

    case ACCESS_GET_PTS_DELAY:
        pi_64 = (int64_t*)va_arg( args, int64_t * );
        *pi_64 = (int64_t)var_GetInteger( p_access, "smb-caching" ) * 1000;
        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:
352
    case ACCESS_GET_CONTENT_TYPE:
353
354
355
356
357
358
359
360
361
362
        return VLC_EGENERIC;

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

    }

    return VLC_SUCCESS;
}
363
364
365
366
367
368
369
370
371
372

#ifdef WIN32
static void Win32AddConnection( access_t *p_access, char *psz_path,
                                char *psz_user, char *psz_pwd,
                                char *psz_domain )
{
    DWORD (*OurWNetAddConnection2)( LPNETRESOURCE, LPCTSTR, LPCTSTR, DWORD );
    char psz_remote[MAX_PATH], psz_server[MAX_PATH], psz_share[MAX_PATH];
    NETRESOURCE net_resource;
    DWORD i_result;
373
    char *psz_parser;
374
    VLC_UNUSED( psz_domain );
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394

    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
395
    strlcpy( psz_server, psz_path, sizeof( psz_server ) );
396
    psz_share[0] = 0;
397
398
399
    psz_parser = strchr( psz_path, '/' );
    if( psz_parser )
    {
400
        char *psz_parser2 = strchr( ++psz_parser, '/' );
401
        if( psz_parser2 )
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
402
            strlcpy( psz_share, psz_parser, sizeof( psz_share ) );
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
403
   }
404

405
    snprintf( psz_remote, sizeof( psz_remote ), "\\\\%s\\%s", psz_server, psz_share );
406
407
408
409
410
411
412
413
    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 );
    }
414
    else if( i_result != ERROR_ALREADY_ASSIGNED &&
415
416
417
418
419
420
421
422
423
424
425
426
             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