access.c 19.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
/*****************************************************************************
 * bdsm/access.c: liBDSM based SMB/CIFS access module
 *****************************************************************************
 * Copyright (C) 2001-2014 VLC authors and VideoLAN
 *
 * Authors: Julien 'Lta' BALLET <contact # lta 'dot' io>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

Thomas Guillem's avatar
Thomas Guillem committed
30 31 32 33
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_services_discovery.h>
#include <vlc_url.h>
34 35
#include <vlc_access.h>
#include <vlc_variables.h>
Thomas Guillem's avatar
Thomas Guillem committed
36
#include <vlc_keystore.h>
37
#include <vlc_network.h>
38

39
#include <assert.h>
40
#include <string.h>
41 42 43 44 45 46
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
# include <netinet/in.h>
# include <arpa/inet.h>
# include <netdb.h>
#endif
47

Thomas Guillem's avatar
Thomas Guillem committed
48
#include <bdsm/bdsm.h>
49
#include "../smb_common.h"
Thomas Guillem's avatar
Thomas Guillem committed
50

51 52 53
/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
Thomas Guillem's avatar
Thomas Guillem committed
54 55 56 57
int bdsm_SdOpen( vlc_object_t * );
void bdsm_SdClose( vlc_object_t * );
int bdsm_sd_probe_Open( vlc_object_t * );

58 59 60
static int Open( vlc_object_t * );
static void Close( vlc_object_t * );

61
VLC_SD_PROBE_HELPER( "dsm", N_("Windows networks"), SD_CAT_LAN )
62 63 64 65 66 67 68 69 70 71

#define BDSM_HELP N_("libdsm's SMB (Windows network shares) input and browser")

vlc_module_begin ()
    set_shortname( "dsm" )
    set_description( N_("libdsm SMB input") )
    set_help(BDSM_HELP)
    set_capability( "access", 20 )
    set_category( CAT_INPUT )
    set_subcategory( SUBCAT_INPUT_ACCESS )
72 73 74
    add_string( "smb-user", NULL, SMB_USER_TEXT, SMB_USER_LONGTEXT, false )
    add_password( "smb-pwd", NULL, SMB_PASS_TEXT, SMB_PASS_LONGTEXT, false )
    add_string( "smb-domain", NULL, SMB_DOMAIN_TEXT, SMB_DOMAIN_LONGTEXT, false )
75 76
    add_shortcut( "smb", "cifs" )
    set_callbacks( Open, Close )
77 78

    add_submodule()
79
        add_shortcut( "dsm-sd" )
80
        set_description( N_("libdsm NETBIOS discovery module") )
81 82 83
        set_category( CAT_PLAYLIST )
        set_subcategory( SUBCAT_PLAYLIST_SD )
        set_capability( "services_discovery", 0 )
84
        set_callbacks( bdsm_SdOpen, bdsm_SdClose )
85 86 87

        VLC_SD_PROBE_SUBMODULE

88 89 90 91 92
vlc_module_end ()

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
93
static ssize_t Read( access_t *, void *, size_t );
94 95
static int Seek( access_t *, uint64_t );
static int Control( access_t *, int, va_list );
Thomas Guillem's avatar
Thomas Guillem committed
96
static int BrowserInit( access_t *p_access );
97

Thomas Guillem's avatar
Thomas Guillem committed
98 99 100
static int get_address( access_t *p_access );
static int login( access_t *p_access );
static bool get_path( access_t *p_access );
101 102
static int add_item( access_t *p_access,  struct access_fsdir *p_fsdir,
                     const char *psz_name, int i_type );
Thomas Guillem's avatar
Thomas Guillem committed
103 104 105 106 107 108 109

struct access_sys_t
{
    netbios_ns         *p_ns;               /**< Netbios name service */
    smb_session        *p_session;          /**< bdsm SMB Session object */

    vlc_url_t           url;
110 111 112
    char               *psz_fullpath;
    const char         *psz_share;
    const char         *psz_path;
Thomas Guillem's avatar
Thomas Guillem committed
113 114 115 116 117 118 119

    char                netbios_name[16];
    struct in_addr      addr;

    smb_fd              i_fd;               /**< SMB fd for the file we're reading */
    smb_tid             i_tid;              /**< SMB Tree ID we're connected to */
};
120 121 122 123

/*****************************************************************************
 * Open: Initialize module's data structures and libdsm
 *****************************************************************************/
124
static int Open( vlc_object_t *p_this )
125 126 127
{
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys;
Thomas Guillem's avatar
Thomas Guillem committed
128
    smb_stat st;
129 130 131 132 133 134

    /* Init p_access */
    p_sys = p_access->p_sys = (access_sys_t*)calloc( 1, sizeof( access_sys_t ) );
    if( p_access->p_sys == NULL )
        return VLC_ENOMEM;

Thomas Guillem's avatar
Thomas Guillem committed
135 136 137 138 139 140 141 142
    p_sys->p_ns = netbios_ns_new();
    if( p_sys->p_ns == NULL )
        goto error;

    p_sys->p_session = smb_session_new();
    if( p_sys->p_session == NULL )
        goto error;

143
    vlc_UrlParse( &p_sys->url, p_access->psz_url );
Thomas Guillem's avatar
Thomas Guillem committed
144
    if( get_address( p_access ) != VLC_SUCCESS )
145 146
        goto error;

Thomas Guillem's avatar
Thomas Guillem committed
147 148 149 150
    msg_Dbg( p_access, "Session: Host name = %s, ip = %s", p_sys->netbios_name,
             inet_ntoa( p_sys->addr ) );

    /* Now that we have the required data, let's establish a session */
151 152 153
    if( smb_session_connect( p_sys->p_session, p_sys->netbios_name,
                             p_sys->addr.s_addr, SMB_TRANSPORT_TCP )
                             != DSM_SUCCESS )
Thomas Guillem's avatar
Thomas Guillem committed
154 155 156 157 158
    {
        msg_Err( p_access, "Unable to connect/negotiate SMB session");
        goto error;
    }

159 160
    get_path( p_access );

Thomas Guillem's avatar
Thomas Guillem committed
161
    if( login( p_access ) != VLC_SUCCESS )
162
    {
163 164
        msg_Err( p_access, "Unable to open file with path %s (in share %s)",
                 p_sys->psz_path, p_sys->psz_share );
Thomas Guillem's avatar
Thomas Guillem committed
165
        goto error;
166
    }
Thomas Guillem's avatar
Thomas Guillem committed
167

168 169
    /* If there is no shares, browse them */
    if( !p_sys->psz_share )
170
        return BrowserInit( p_access );
Thomas Guillem's avatar
Thomas Guillem committed
171

172 173
    assert(p_sys->i_fd > 0);

174 175 176
    msg_Dbg( p_access, "Path: Share name = %s, path = %s", p_sys->psz_share,
             p_sys->psz_path );

Thomas Guillem's avatar
Thomas Guillem committed
177
    st = smb_stat_fd( p_sys->p_session, p_sys->i_fd );
178 179 180 181 182 183 184
    if( smb_stat_get( st, SMB_STAT_ISDIR ) )
    {
        smb_fclose( p_sys->p_session, p_sys->i_fd );
        return BrowserInit( p_access );
    }

    msg_Dbg( p_access, "Successfully opened smb://%s", p_access->psz_location );
Thomas Guillem's avatar
Thomas Guillem committed
185

186 187 188 189
    ACCESS_SET_CALLBACKS( Read, NULL, Control, Seek );
    return VLC_SUCCESS;

    error:
Thomas Guillem's avatar
Thomas Guillem committed
190
        Close( p_this );
191 192 193 194 195 196
        return VLC_EGENERIC;
}

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

Thomas Guillem's avatar
Thomas Guillem committed
202 203 204 205 206 207
    if( p_sys->p_ns )
        netbios_ns_destroy( p_sys->p_ns );
    if( p_sys->i_fd )
        smb_fclose( p_sys->p_session, p_sys->i_fd );
    if( p_sys->p_session )
        smb_session_destroy( p_sys->p_session );
Hugo Beauzée-Luyssen's avatar
Hugo Beauzée-Luyssen committed
208
    vlc_UrlClean( &p_sys->url );
209
    free( p_sys->psz_fullpath );
210

211 212 213 214 215 216 217
    free( p_sys );
}

/*****************************************************************************
 * Local functions
 *****************************************************************************/

Thomas Guillem's avatar
Thomas Guillem committed
218 219
/* Returns VLC_EGENERIC if it wasn't able to get an ip address to connect to */
static int get_address( access_t *p_access )
220
{
Thomas Guillem's avatar
Thomas Guillem committed
221 222
    access_sys_t *p_sys = p_access->p_sys;

223 224
    if( p_sys->url.psz_host != NULL &&
        !inet_pton( AF_INET, p_sys->url.psz_host, &p_sys->addr ) )
225 226 227 228 229
    {
        /* This is not an ip address, let's try netbios/dns resolve */
        struct addrinfo *p_info = NULL;

        /* Is this a netbios name on this LAN ? */
230 231
        if( netbios_ns_resolve( p_sys->p_ns, p_sys->url.psz_host,
                                NETBIOS_FILESERVER,
232
                                &p_sys->addr.s_addr) == 0 )
233 234
        {
            strlcpy( p_sys->netbios_name, p_sys->url.psz_host, 16);
Thomas Guillem's avatar
Thomas Guillem committed
235
            return VLC_SUCCESS;
236 237 238 239 240 241 242 243 244 245
        }
        /* or is it an existing dns name ? */
        else if( getaddrinfo( p_sys->url.psz_host, NULL, NULL, &p_info ) == 0 )
        {
            if( p_info->ai_family == AF_INET )
            {
                struct sockaddr_in *in = (struct sockaddr_in *)p_info->ai_addr;
                p_sys->addr.s_addr = in->sin_addr.s_addr;
            }
            if( p_info->ai_family != AF_INET )
246 247
            {
                freeaddrinfo( p_info );
Thomas Guillem's avatar
Thomas Guillem committed
248
                return VLC_EGENERIC;
249 250
            }
            freeaddrinfo( p_info );
251 252
        }
        else
Thomas Guillem's avatar
Thomas Guillem committed
253
            return VLC_EGENERIC;
254 255 256 257 258 259 260 261 262 263 264 265 266
    }

    /* We have an IP address, let's find the NETBIOS name */
    const char *psz_nbt = netbios_ns_inverse( p_sys->p_ns, p_sys->addr.s_addr );
    if( psz_nbt != NULL )
        strlcpy( p_sys->netbios_name, psz_nbt, 16 );
    else
    {
        msg_Warn( p_access, "Unable to get netbios name of %s",
            p_sys->url.psz_host );
        p_sys->netbios_name[0] = '\0';
    }

Thomas Guillem's avatar
Thomas Guillem committed
267
    return VLC_SUCCESS;
268 269
}

Thomas Guillem's avatar
Thomas Guillem committed
270
static int smb_connect( access_t *p_access, const char *psz_login,
271
                        const char *psz_password, const char *psz_domain)
272
{
Thomas Guillem's avatar
Thomas Guillem committed
273 274
    access_sys_t *p_sys = p_access->p_sys;

275
    smb_session_set_creds( p_sys->p_session, psz_domain,
276
                           psz_login, psz_password );
277
    if( smb_session_login( p_sys->p_session ) == DSM_SUCCESS )
278 279 280 281
    {
        if( p_sys->psz_share )
        {
            /* Connect to the share */
282 283
            if( smb_tree_connect( p_sys->p_session, p_sys->psz_share,
                                  &p_sys->i_tid ) != DSM_SUCCESS )
284
                return VLC_EGENERIC;
285 286

            /* Let's finally ask a handle to the file we wanna read ! */
287 288 289
            return smb_fopen( p_sys->p_session, p_sys->i_tid, p_sys->psz_path,
                              SMB_MOD_RO, &p_sys->i_fd )
                              == DSM_SUCCESS ? VLC_SUCCESS : VLC_EGENERIC;
290
        }
291 292
        else
            return VLC_SUCCESS;
293 294 295
    }
    else
        return VLC_EGENERIC;
296 297
}

298 299 300 301 302
static bool smb_has_invalid_creds( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;
    uint32_t i_nt_status = smb_session_get_nt_status( p_sys->p_session );

303
    return i_nt_status & (NT_STATUS_ACCESS_DENIED | NT_STATUS_LOGON_FAILURE);
304 305
}

306 307
/* Performs login with existing credentials and ask the user for new ones on
   failure */
Thomas Guillem's avatar
Thomas Guillem committed
308
static int login( access_t *p_access )
309
{
Thomas Guillem's avatar
Thomas Guillem committed
310
    int i_ret = VLC_EGENERIC;
Thomas Guillem's avatar
Thomas Guillem committed
311
    access_sys_t *p_sys = p_access->p_sys;
Thomas Guillem's avatar
Thomas Guillem committed
312 313 314
    vlc_credential credential;
    char *psz_var_domain;
    const char *psz_login, *psz_password, *psz_domain;
315
    bool b_guest = false;
Thomas Guillem's avatar
Thomas Guillem committed
316

317
    vlc_credential_init( &credential, &p_sys->url );
Thomas Guillem's avatar
Thomas Guillem committed
318
    psz_var_domain = var_InheritString( p_access, "smb-domain" );
319
    credential.psz_realm = psz_var_domain ? psz_var_domain : NULL;
Thomas Guillem's avatar
Thomas Guillem committed
320 321 322 323 324 325 326

    vlc_credential_get( &credential, p_access, "smb-user", "smb-pwd",
                        NULL, NULL );

    if( !credential.psz_username )
    {
        psz_login = "Guest";
327
        psz_password = "";
328
        b_guest = true;
Thomas Guillem's avatar
Thomas Guillem committed
329 330 331 332 333 334
    }
    else
    {
        psz_login = credential.psz_username;
        psz_password = credential.psz_password;
    }
335
    psz_domain = credential.psz_realm ? credential.psz_realm : p_sys->netbios_name;
Thomas Guillem's avatar
Thomas Guillem committed
336

337
    /* Try to authenticate on the remote machine */
Thomas Guillem's avatar
Thomas Guillem committed
338 339
    if( smb_connect( p_access, psz_login, psz_password, psz_domain )
                     != VLC_SUCCESS )
340
    {
341 342
        while( smb_has_invalid_creds( p_access)
            && vlc_credential_get( &credential, p_access, "smb-user", "smb-pwd",
343 344
                                   SMB_LOGIN_DIALOG_TITLE,
                                   SMB_LOGIN_DIALOG_TEXT, p_sys->netbios_name ) )
345
        {
346
            b_guest = false;
Thomas Guillem's avatar
Thomas Guillem committed
347 348
            psz_login = credential.psz_username;
            psz_password = credential.psz_password;
349 350
            psz_domain = credential.psz_realm ? credential.psz_realm
                                              : p_sys->netbios_name;
Thomas Guillem's avatar
Thomas Guillem committed
351 352 353
            if( smb_connect( p_access, psz_login, psz_password, psz_domain )
                             == VLC_SUCCESS )
                goto success;
354 355
        }

356
        msg_Err( p_access, "Unable to login" );
Thomas Guillem's avatar
Thomas Guillem committed
357
        goto error;
358
    }
359
    else if( smb_session_is_guest( p_sys->p_session ) == 1 )
360
    {
361
        msg_Warn( p_access, "Login failure but you were logged in as a Guest");
362 363
        b_guest = true;
    }
364

Thomas Guillem's avatar
Thomas Guillem committed
365
success:
366
    msg_Warn( p_access, "Creds: username = '%s', domain = '%s'",
Thomas Guillem's avatar
Thomas Guillem committed
367
             psz_login, psz_domain );
368
    if( !b_guest )
369
        vlc_credential_store( &credential, p_access );
370

Thomas Guillem's avatar
Thomas Guillem committed
371 372 373 374 375
    i_ret = VLC_SUCCESS;
error:
    vlc_credential_clean( &credential );
    free( psz_var_domain );
    return i_ret;
376 377
}

378
static void backslash_path( char *psz_path )
379
{
380
    char *iter = psz_path;
381 382 383 384 385 386 387 388 389 390 391

    /* Let's switch the path delimiters from / to \ */
    while( *iter != '\0' )
    {
        if( *iter == '/' )
            *iter = '\\';
        iter++;
    }
}

/* Get the share and filepath from uri (also replace all / by \ in url.psz_path) */
Thomas Guillem's avatar
Thomas Guillem committed
392
static bool get_path( access_t *p_access )
393
{
Thomas Guillem's avatar
Thomas Guillem committed
394
    access_sys_t *p_sys = p_access->p_sys;
395 396 397 398 399
    char *iter;

    if( p_sys->url.psz_path == NULL )
        return false;

400 401 402 403 404
    p_sys->psz_fullpath = vlc_uri_decode_duplicate( p_sys->url.psz_path );
    if( p_sys->psz_fullpath == NULL )
        return false;

    backslash_path( p_sys->psz_fullpath );
405 406

    /* Is path longer than just "/" ? */
407
    if( strlen( p_sys->psz_fullpath ) > 1 )
408
    {
409
        iter = p_sys->psz_fullpath;
410 411
        while( *iter == '\\' ) iter++; /* Handle smb://Host/////Share/ */

412
        p_sys->psz_share = iter;
413 414 415 416 417 418 419 420 421 422 423 424
    }
    else
    {
        msg_Dbg( p_access, "no share, nor file path provided, will switch to browser");
        return false;
    }

    iter = strchr( p_sys->psz_share, '\\' );
    if( iter == NULL || strlen(iter + 1) == 0 )
    {
        if( iter != NULL ) /* Remove the trailing \ */
            *iter = '\0';
425
        p_sys->psz_path = "";
426 427 428 429 430

        msg_Dbg( p_access, "no file path provided, will switch to browser ");
        return true;
    }

431
    p_sys->psz_path = iter + 1; /* Skip the first \ */
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448
    *iter = '\0';

    return true;
}

/*****************************************************************************
 * Seek: try to go at the right place
 *****************************************************************************/
static int Seek( access_t *p_access, uint64_t i_pos )
{
    access_sys_t *p_sys = p_access->p_sys;

    if( i_pos >= INT64_MAX )
        return VLC_EGENERIC;

    msg_Dbg( p_access, "seeking to %"PRId64, i_pos );

449 450
    if (smb_fseek(p_sys->p_session, p_sys->i_fd, i_pos, SMB_SEEK_SET) == -1)
        return VLC_EGENERIC;
451 452 453 454 455 456 457

    return VLC_SUCCESS;
}

/*****************************************************************************
 * Read:
 *****************************************************************************/
458
static ssize_t Read( access_t *p_access, void *p_buffer, size_t i_len )
459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
{
    access_sys_t *p_sys = p_access->p_sys;
    int i_read;

    i_read = smb_fread( p_sys->p_session, p_sys->i_fd, p_buffer, i_len );
    if( i_read < 0 )
    {
        msg_Err( p_access, "read failed" );
        return -1;
    }

    return i_read;
}

/*****************************************************************************
 * Control:
 *****************************************************************************/
static int Control( access_t *p_access, int i_query, va_list args )
{
478
    access_sys_t *p_sys = p_access->p_sys;
479

480 481
    switch( i_query )
    {
482 483 484
    case STREAM_CAN_SEEK:
    case STREAM_CAN_PAUSE:
    case STREAM_CAN_CONTROL_PACE:
485 486 487
        *va_arg( args, bool* ) = true;
        break;

488
    case STREAM_CAN_FASTSEEK:
489 490 491
        *va_arg( args, bool* ) = false;
        break;

492
    case STREAM_GET_SIZE:
493
    {
494
        smb_stat st = smb_stat_fd( p_sys->p_session, p_sys->i_fd );
495 496 497
        *va_arg( args, uint64_t * ) = smb_stat_get( st, SMB_STAT_SIZE );
        break;
    }
498
    case STREAM_GET_PTS_DELAY:
499 500 501 502
        *va_arg( args, int64_t * ) = INT64_C(1000)
            * var_InheritInteger( p_access, "network-caching" );
        break;

503
    case STREAM_SET_PAUSE_STATE:
504 505 506 507 508 509 510 511 512
        /* Nothing to do */
        break;

    default:
        return VLC_EGENERIC;
    }

    return VLC_SUCCESS;
}
Thomas Guillem's avatar
Thomas Guillem committed
513

514 515
static int add_item( access_t *p_access, struct access_fsdir *p_fsdir,
                     const char *psz_name, int i_type )
Thomas Guillem's avatar
Thomas Guillem committed
516
{
517
    char         *psz_uri;
Thomas Guillem's avatar
Thomas Guillem committed
518 519
    int           i_ret;

Thomas Guillem's avatar
Thomas Guillem committed
520 521
    char *psz_encoded_name = vlc_uri_encode( psz_name );
    if( psz_encoded_name == NULL )
522
        return VLC_ENOMEM;
523 524 525 526 527
    const char *psz_sep = p_access->psz_location[0] != '\0'
        && p_access->psz_location[strlen(p_access->psz_location) -1] != '/'
        ? "/" : "";
    i_ret = asprintf( &psz_uri, "smb://%s%s%s", p_access->psz_location,
                      psz_sep, psz_encoded_name );
Thomas Guillem's avatar
Thomas Guillem committed
528
    free( psz_encoded_name );
Thomas Guillem's avatar
Thomas Guillem committed
529
    if( i_ret == -1 )
530
        return VLC_ENOMEM;
Thomas Guillem's avatar
Thomas Guillem committed
531

532
    return access_fsdir_additem( p_fsdir, psz_uri, psz_name, i_type, ITEM_NET );
Thomas Guillem's avatar
Thomas Guillem committed
533 534
}

535
static int BrowseShare( access_t *p_access, input_item_node_t *p_node )
Thomas Guillem's avatar
Thomas Guillem committed
536 537
{
    access_sys_t *p_sys = p_access->p_sys;
538
    smb_share_list  shares;
Thomas Guillem's avatar
Thomas Guillem committed
539
    const char     *psz_name;
540 541
    size_t          share_count;
    int             i_ret = VLC_SUCCESS;
Thomas Guillem's avatar
Thomas Guillem committed
542

543 544 545 546 547 548 549 550
    if( smb_share_get_list( p_sys->p_session, &shares, &share_count )
        != DSM_SUCCESS )
        return VLC_EGENERIC;

    struct access_fsdir fsdir;
    access_fsdir_init( &fsdir, p_access, p_node );

    for( size_t i = 0; i < share_count && i_ret == VLC_SUCCESS; i++ )
Thomas Guillem's avatar
Thomas Guillem committed
551
    {
552
        psz_name = smb_share_list_at( shares, i );
Thomas Guillem's avatar
Thomas Guillem committed
553 554 555 556

        if( psz_name[strlen( psz_name ) - 1] == '$')
            continue;

557
        i_ret = add_item( p_access, &fsdir, psz_name, ITEM_TYPE_DIRECTORY );
Thomas Guillem's avatar
Thomas Guillem committed
558
    }
559 560 561 562 563

    access_fsdir_finish( &fsdir, i_ret == VLC_SUCCESS );

    smb_share_list_destroy( shares );
    return i_ret;
Thomas Guillem's avatar
Thomas Guillem committed
564 565
}

566
static int BrowseDirectory( access_t *p_access, input_item_node_t *p_node )
Thomas Guillem's avatar
Thomas Guillem committed
567 568
{
    access_sys_t *p_sys = p_access->p_sys;
569
    smb_stat_list   files;
Thomas Guillem's avatar
Thomas Guillem committed
570 571 572
    smb_stat        st;
    char           *psz_query;
    const char     *psz_name;
573 574
    size_t          files_count;
    int             i_ret = VLC_SUCCESS;
Thomas Guillem's avatar
Thomas Guillem committed
575

576
    if( p_sys->psz_path != NULL )
Thomas Guillem's avatar
Thomas Guillem committed
577
    {
578 579 580 581
        if( asprintf( &psz_query, "%s\\*", p_sys->psz_path ) == -1 )
            return VLC_ENOMEM;
        files = smb_find( p_sys->p_session, p_sys->i_tid, psz_query );
        free( psz_query );
Thomas Guillem's avatar
Thomas Guillem committed
582
    }
583 584 585 586 587 588 589 590
    else
        files = smb_find( p_sys->p_session, p_sys->i_tid, "\\*" );

    if( files == NULL )
        return VLC_EGENERIC;

    struct access_fsdir fsdir;
    access_fsdir_init( &fsdir, p_access, p_node );
Thomas Guillem's avatar
Thomas Guillem committed
591

592 593
    files_count = smb_stat_list_count( files );
    for( size_t i = 0; i < files_count && i_ret == VLC_SUCCESS; i++ )
Thomas Guillem's avatar
Thomas Guillem committed
594
    {
595 596
        int i_type;

597
        st = smb_stat_list_at( files, i );
Thomas Guillem's avatar
Thomas Guillem committed
598

Thomas Guillem's avatar
Thomas Guillem committed
599
        if( st == NULL )
600 601 602
        {
            continue;
        }
Thomas Guillem's avatar
Thomas Guillem committed
603 604 605

        psz_name = smb_stat_name( st );

606 607
        i_type = smb_stat_get( st, SMB_STAT_ISDIR ) ?
                 ITEM_TYPE_DIRECTORY : ITEM_TYPE_FILE;
608
        i_ret = add_item( p_access, &fsdir, psz_name, i_type );
Thomas Guillem's avatar
Thomas Guillem committed
609
    }
610 611 612 613 614

    access_fsdir_finish( &fsdir, i_ret == VLC_SUCCESS );

    smb_stat_list_destroy( files );
    return i_ret;
Thomas Guillem's avatar
Thomas Guillem committed
615 616
}

617 618 619 620
static int DirControl( access_t *p_access, int i_query, va_list args )
{
    switch( i_query )
    {
621
    case STREAM_IS_DIRECTORY:
622 623 624 625 626 627 628 629 630 631
        *va_arg( args, bool * ) = p_access->pf_readdir == BrowseDirectory;
                                  /* might loop */
        break;
    default:
        return access_vaDirectoryControlHelper( p_access, i_query, args );
    }

    return VLC_SUCCESS;
}

Thomas Guillem's avatar
Thomas Guillem committed
632 633 634 635 636 637 638 639
static int BrowserInit( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;

    if( p_sys->psz_share == NULL )
        p_access->pf_readdir = BrowseShare;
    else
        p_access->pf_readdir = BrowseDirectory;
640
    p_access->pf_control = DirControl;
Thomas Guillem's avatar
Thomas Guillem committed
641 642 643

    return VLC_SUCCESS;
}