[vlc-devel] [RFC PATCH 3/8] dsm: refactorize

Thomas Guillem thomas at gllm.fr
Thu Nov 27 12:00:13 CET 2014


Move browser.c to common.c. This new file contains common browsing functions
that can be used from an access_t or from a services_discovery_t.
---
 modules/access/Makefile.am   |   2 +-
 modules/access/dsm/access.c  | 340 ++++++------------------------
 modules/access/dsm/browser.c | 191 -----------------
 modules/access/dsm/common.c  | 483 +++++++++++++++++++++++++++++++++++++++++++
 modules/access/dsm/common.h  |  25 ++-
 5 files changed, 572 insertions(+), 469 deletions(-)
 delete mode 100644 modules/access/dsm/browser.c
 create mode 100644 modules/access/dsm/common.c

diff --git a/modules/access/Makefile.am b/modules/access/Makefile.am
index e154bee..528ddc0 100644
--- a/modules/access/Makefile.am
+++ b/modules/access/Makefile.am
@@ -413,7 +413,7 @@ access_LTLIBRARIES += $(LTLIBsmb)
 EXTRA_LTLIBRARIES += libsmb_plugin.la
 
 libdsm_plugin_la_SOURCES = access/dsm/access.c access/dsm/common.h \
-	access/dsm/browser.c access/dsm/sd.c
+	access/dsm/common.c access/dsm/sd.c
 libdsm_plugin_la_CFLAGS = $(AM_CFLAGS) $(DSM_CFLAGS)
 libdsm_plugin_la_LIBADD = $(DSM_LIBS)
 libdsm_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(sddir)'
diff --git a/modules/access/dsm/access.c b/modules/access/dsm/access.c
index 6269b77..5ec3b2a 100644
--- a/modules/access/dsm/access.c
+++ b/modules/access/dsm/access.c
@@ -33,12 +33,6 @@
 #include <vlc_variables.h>
 #include <vlc_dialog.h>
 
-#include <string.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
@@ -87,15 +81,9 @@ static ssize_t Read( access_t *, uint8_t *, size_t );
 static int Seek( access_t *, uint64_t );
 static int Control( access_t *, int, va_list );
 
-static void split_domain_login( char **psz_login, char **psz_domain );
-static void get_credentials( access_t *p_access, access_sys_t *p_sys );
-static bool get_address( access_t *p_access, access_sys_t *p_sys );
 static void login_dialog( access_t *p_access, access_sys_t *p_sys );
-static bool login( access_t *p_access, access_sys_t *p_sys );
-static void backslash_path( vlc_url_t *p_url );
-static bool get_path( access_t *p_access, access_sys_t *p_sys );
-static bool bdsm_init( access_t *p_access, access_sys_t *p_sys );
-static void bdsm_destroy( access_sys_t *p_sys );
+static void login( void *p_ctx );
+static int BrowserInit( access_t *p_access );
 
 /*****************************************************************************
  * Dialog strings
@@ -112,6 +100,7 @@ int Open( vlc_object_t *p_this )
 {
     access_t     *p_access = (access_t*)p_this;
     access_sys_t *p_sys;
+    bdsm_login_cb_t login_cb;
 
     /* Init p_access */
     access_InitFields( p_access );
@@ -119,46 +108,24 @@ int Open( vlc_object_t *p_this )
     if( p_access->p_sys == NULL )
         return VLC_ENOMEM;
 
-    if( !bdsm_init( p_access, p_sys ) )
-        goto error;
-
-    if( !get_path( p_access, p_sys ) )
-        return BrowserInit( p_access );
-    msg_Dbg( p_access, "Path: Share name = %s, path = %s", p_sys->psz_share,
-             p_sys->psz_path );
-
-
-    /* Connect to the share */
-    p_sys->i_tid = smb_tree_connect( p_sys->p_session, p_sys->psz_share );
-    if( !p_sys->i_tid )
-    {
-        msg_Err( p_access, "Unable to connect to share %s", p_sys->psz_share );
+    if( bdsm_Init( p_sys, VLC_OBJECT(p_access) ) != VLC_SUCCESS )
         goto error;
-    }
 
-    /* Let's finally ask a handle to the file we wanna read ! */
-    p_sys->i_fd = smb_fopen( p_sys->p_session, p_sys->i_tid, p_sys->psz_path,
-                             SMB_MOD_RO );
-    if( !p_sys->i_fd )
-    {
-        msg_Err( p_access, "Unable to open file with path %s (in share %s)",
-                 p_sys->psz_path, p_sys->psz_share );
+    login_cb.pf_login = login;
+    login_cb.p_ctx = p_access;
+    if( bdsm_Connect( p_sys, &login_cb, p_access->psz_location ) != VLC_SUCCESS )
         goto error;
-    }
 
-    smb_stat st = smb_stat_fd( p_sys->p_session, p_sys->i_fd );
-    if( smb_stat_get( st, SMB_STAT_ISDIR ) )
-    {
-        smb_fclose( p_sys->p_session, p_sys->i_fd );
+    if( p_sys->b_is_browsing )
         return BrowserInit( p_access );
-    }
 
     msg_Dbg( p_access, "Successfully opened smb://%s", p_access->psz_location );
+
     ACCESS_SET_CALLBACKS( Read, NULL, Control, Seek );
     return VLC_SUCCESS;
 
     error:
-        bdsm_destroy( p_sys );
+        bdsm_Destroy( p_sys );
         free( p_sys );
         return VLC_EGENERIC;
 }
@@ -171,8 +138,7 @@ void Close( vlc_object_t *p_this )
     access_t     *p_access = (access_t*)p_this;
     access_sys_t *p_sys = p_access->p_sys;
 
-    smb_fclose( p_sys->p_session, p_sys->i_fd );
-    bdsm_destroy( p_sys );
+    bdsm_Destroy( p_sys );
     free( p_sys );
 }
 
@@ -180,96 +146,6 @@ void Close( vlc_object_t *p_this )
  * Local functions
  *****************************************************************************/
 
-/* Split DOMAIN\User if it finds a '\' in psz_login. */
-static void split_domain_login( char **psz_login, char **psz_domain )
-{
-    char *user = strchr( *psz_login, '\\' );
-
-    if( user != NULL )
-    {
-        *psz_domain = *psz_login;
-        *user = '\0';
-        *psz_login = strdup( user + 1 );
-    }
-}
-
-/* Get credentials from uri or variables. */
-static void get_credentials( access_t *p_access, access_sys_t *p_sys )
-{
-    /* Fetch credentials, either from URI or from options if not provided */
-    if( p_sys->url.psz_password == NULL )
-        p_sys->creds.password = var_InheritString( p_access, "smb-pwd" );
-    else
-        p_sys->creds.password = strdup( p_sys->url.psz_password );
-
-    /* Here we support smb://DOMAIN\User:password@XXX, get user from options
-       or default to "Guest" as last resort */
-    if( p_sys->url.psz_username != NULL )
-    {
-        p_sys->creds.login = strdup( p_sys->url.psz_username );
-        split_domain_login( &p_sys->creds.login, &p_sys->creds.domain );
-    }
-    else
-    {
-        p_sys->creds.login = var_InheritString( p_access, "smb-user" );
-        if( p_sys->creds.login == NULL )
-            p_sys->creds.login = strdup( "Guest" );
-    }
-
-    if( p_sys->creds.domain == NULL )
-        p_sys->creds.domain = var_InheritString( p_access, "smb-domain" );
-
-}
-
-/* Returns false if it wasn't able to get an ip address to connect to */
-static bool get_address( access_t *p_access, access_sys_t *p_sys )
-{
-    if( !inet_aton( p_sys->url.psz_host, &p_sys->addr ) )
-    {
-        /* 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 ? */
-        if( netbios_ns_resolve( p_sys->p_ns, p_sys->url.psz_host, 0x20,
-                                &p_sys->addr.s_addr) )
-        {
-            strlcpy( p_sys->netbios_name, p_sys->url.psz_host, 16);
-            return true;
-        }
-        /* 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;
-            }
-            freeaddrinfo( p_info );
-            if( p_info->ai_family != AF_INET )
-                return false;
-        }
-        else
-            return false;
-    }
-
-    /* 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';
-    }
-
-    /* If no domain was explicitly specified, let's use the machine name */
-    if( p_sys->creds.domain == NULL && p_sys->netbios_name[0] )
-        p_sys->creds.domain = strdup( p_sys->netbios_name );
-
-    return true;
-}
-
 /* Displays a dialog for the user to enter his/her credentials */
 static void login_dialog( access_t *p_access, access_sys_t *p_sys )
 {
@@ -290,7 +166,7 @@ static void login_dialog( access_t *p_access, access_sys_t *p_sys )
         if( p_sys->creds.login != NULL )
             free( p_sys->creds.login );
         p_sys->creds.login = psz_login;
-        split_domain_login( &p_sys->creds.login, &p_sys->creds.domain );
+        bdsm_SplitDomainLogin( &p_sys->creds.login, &p_sys->creds.domain );
     }
 
     if( psz_pass != NULL )
@@ -301,164 +177,78 @@ static void login_dialog( access_t *p_access, access_sys_t *p_sys )
     }
 }
 
-/* Performs login with existing credentials and ask the user for new ones on
-   failure */
-static bool login( access_t *p_access, access_sys_t *p_sys )
+static void login( void *p_ctx )
 {
-    if( p_sys->creds.login == NULL )
-        p_sys->creds.login = strdup( "Guest" );
-    if( p_sys->creds.password == NULL )
-        p_sys->creds.password = strdup( "Guest" );
-    if( p_sys->creds.domain == NULL )
-        p_sys->creds.domain = strdup( "WORKGROUP" );
-
-    /* Try to authenticate on the remote machine */
-    smb_session_set_creds( p_sys->p_session, p_sys->creds.domain,
-                           p_sys->creds.login, p_sys->creds.password );
-    if( !smb_session_login( p_sys->p_session ) )
-    {
-        for( int i = 0; i < BDSM_LOGIN_DIALOG_RETRY; i++ )
-        {
-            login_dialog( p_access, p_sys );
-            smb_session_set_creds( p_sys->p_session, p_sys->creds.domain,
-                                   p_sys->creds.login, p_sys->creds.password );
-            if( smb_session_login( p_sys->p_session ) )
-                return true;
-        }
-
-        /* FIXME, Try to force netbios name as domain then WORKGROUP here */
-        msg_Err( p_access, "Unable to login with username = %s, password = %s, domain = %s",
-                   p_sys->creds.login, p_sys->creds.password,
-                   p_sys->creds.domain );
-        return false;
-    }
-    else if( smb_session_is_guest( p_sys->p_session ) == 1 )
-        msg_Warn( p_access, "Login failure but you were logged in as a Guest");
+    access_t *p_access = p_ctx;
+    access_sys_t *p_sys = p_access->p_sys;
 
-    return true;
+    for( int i = 0; i < BDSM_LOGIN_DIALOG_RETRY; i++ )
+        login_dialog( p_access, p_sys );
 }
 
-static void backslash_path( vlc_url_t *p_url )
+static int AddToNode( void *p_ctx, input_item_t *p_item )
 {
-    char *iter = p_url->psz_path;
-
-    /* Let's switch the path delimiters from / to \ */
-    while( *iter != '\0' )
-    {
-        if( *iter == '/' )
-            *iter = '\\';
-        iter++;
-    }
+    input_item_node_t *p_node = p_ctx;
+    input_item_CopyOptions( p_node->p_item, p_item );
+    return input_item_node_AppendItem( p_node, p_item ) != NULL ? VLC_SUCCESS
+                                                                :  VLC_EGENERIC;
 }
 
-/* Get the share and filepath from uri (also replace all / by \ in url.psz_path) */
-static bool get_path( access_t *p_access, access_sys_t *p_sys )
+static int BrowseShare( access_t *p_access, input_item_node_t *p_node )
 {
-    VLC_UNUSED( p_access );
-
-    char *iter;
-
-    if( p_sys->url.psz_path == NULL )
-        return false;
-
-    backslash_path( &p_sys->url );
-
-    /* Is path longer than just "/" ? */
-    if( strlen( p_sys->url.psz_path ) > 1 )
-    {
-        iter = p_sys->url.psz_path;
-        while( *iter == '\\' ) iter++; /* Handle smb://Host/////Share/ */
+    bdsm_item_cb_t cb;
+    cb.pf_add_item = AddToNode;
+    cb.p_ctx = p_node;
+    return bdsm_BrowseShare( p_access->p_sys, &cb, p_node->p_item->psz_uri );
+}
 
-        p_sys->psz_share = strdup( iter );
-        if ( p_sys->psz_share == NULL )
-            return false;
-    }
-    else
-    {
-        msg_Dbg( p_access, "no share, nor file path provided, will switch to browser");
-        return false;
-    }
+static int BrowseDirectory( access_t *p_access, input_item_node_t *p_node )
+{
+    bdsm_item_cb_t cb;
+    cb.pf_add_item = AddToNode;
+    cb.p_ctx = p_node;
+    return bdsm_BrowseDirectory( p_access->p_sys, &cb, p_node->p_item->psz_uri );
+}
 
+static int BrowserControl( access_t *p_access, int i_query, va_list args )
+{
+    VLC_UNUSED( p_access );
 
-    iter = strchr( p_sys->psz_share, '\\' );
-    if( iter == NULL || strlen(iter + 1) == 0 )
+    switch( i_query )
     {
-        if( iter != NULL ) /* Remove the trailing \ */
-            *iter = '\0';
-        p_sys->psz_path = strdup( "" );
-
-        msg_Dbg( p_access, "no file path provided, will switch to browser ");
-        return true;
-    }
-
-    p_sys->psz_path = strdup( iter + 1); /* Skip the first \ */
-    *iter = '\0';
-    if( p_sys->psz_path == NULL )
-        return false;
-
-    return true;
+        case ACCESS_CAN_SEEK:
+        case ACCESS_CAN_FASTSEEK:
+            *va_arg( args, bool* ) = false;
+            break;
+
+        case ACCESS_CAN_PAUSE:
+        case ACCESS_CAN_CONTROL_PACE:
+            *va_arg( args, bool* ) = true;
+            break;
+
+        case ACCESS_GET_PTS_DELAY:
+            *va_arg( args, int64_t * ) = DEFAULT_PTS_DELAY * 1000;
+            break;
+
+        default:
+            return VLC_EGENERIC;
+     }
+     return VLC_SUCCESS;
 }
 
-/* get ip address/path/creds, then connects and try to authenticate */
-static bool bdsm_init( access_t *p_access, access_sys_t *p_sys )
+static int BrowserInit( access_t *p_access )
 {
-    p_sys->url.psz_buffer = NULL;
-    p_sys->creds.login    = NULL;
-    p_sys->creds.password = NULL;
-    p_sys->creds.domain   = NULL;
-    p_sys->psz_share      = NULL;
-    p_sys->psz_path       = NULL;
-    p_sys->i_fd           = 0;
-    p_sys->i_tid          = 0;
-
-    p_sys->p_session = smb_session_new();
-    if( p_sys->p_session == NULL )
-        return false;
-
-    p_sys->p_ns = netbios_ns_new();
-    if( p_sys->p_ns == NULL )
-        return false;
-
-    vlc_UrlParse( &p_sys->url, p_access->psz_location, 0 );
-    get_credentials( p_access, p_sys );
-    if( !get_address( p_access, p_sys ) )
-        return false;
-
-    msg_Dbg( p_access, "Creds: username = %s, password = %s, domain = %s",
-             p_sys->creds.login, p_sys->creds.password,
-             p_sys->creds.domain );
-    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 */
-    if( !smb_session_connect( p_sys->p_session, p_sys->netbios_name,
-                              p_sys->addr.s_addr, SMB_TRANSPORT_TCP ))
-    {
-        msg_Err( p_access, "Unable to connect/negotiate SMB session");
-        return false;
-    }
-
-    if( !login( p_access, p_sys ) )
-        return false;
-
+    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;
+    p_access->pf_control = BrowserControl;
 
-    return true;
+    return VLC_SUCCESS;
 }
 
-/* Cleanup libdsm stuff */
-static void bdsm_destroy( access_sys_t *p_sys )
-{
-    smb_session_destroy( p_sys->p_session );
-    netbios_ns_destroy( p_sys->p_ns );
-
-    free( p_sys->creds.login );
-    free( p_sys->creds.password );
-    free( p_sys->creds.domain );
-    free( p_sys->url.psz_buffer );
-    free( p_sys->psz_share );
-    free( p_sys->psz_path );
-}
 
 /*****************************************************************************
  * Seek: try to go at the right place
diff --git a/modules/access/dsm/browser.c b/modules/access/dsm/browser.c
deleted file mode 100644
index 40dd314..0000000
--- a/modules/access/dsm/browser.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*****************************************************************************
- * 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
-
-#include "common.h"
-
-static int Control (access_t *p_access, int i_query, va_list args);
-static int BrowseShare( access_t *p_access, input_item_node_t *p_node );
-static int BrowseDirectory( access_t *p_access, input_item_node_t *p_node );
-static bool AddToNode( access_t *p_access, input_item_node_t *p_node,
-                       const char *psz_name );
-
-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;
-    p_access->pf_control = Control;
-
-    return VLC_SUCCESS;
-}
-
-static int BrowseShare( access_t *p_access, input_item_node_t *p_node )
-{
-    smb_share_list  shares;
-    const char     *psz_name;
-    size_t          share_count;
-
-    share_count = smb_share_get_list( p_access->p_sys->p_session, &shares );
-    if( !share_count )
-        return VLC_ENOITEM;
-
-    for( size_t i = 0; i < share_count; i++ )
-    {
-        psz_name = smb_share_list_at( shares, i );
-
-        if( psz_name[strlen( psz_name ) - 1] == '$')
-            continue;
-
-        AddToNode( p_access, p_node, psz_name );
-    }
-
-    smb_share_list_destroy( shares );
-    return VLC_SUCCESS;
-}
-
-static int BrowseDirectory( access_t *p_access, input_item_node_t *p_node )
-{
-    access_sys_t   *p_sys = p_access->p_sys;
-    smb_stat_list   files;
-    smb_stat        st;
-    char           *psz_query;
-    const char     *psz_name;
-    size_t          files_count;
-    int             i_ret;
-
-    if( p_sys->psz_path != NULL )
-    {
-        i_ret = asprintf( &psz_query, "%s\\*", p_sys->psz_path );
-        if( i_ret == -1 )
-            return VLC_ENOMEM;
-        files = smb_find( p_sys->p_session, p_sys->i_tid, psz_query );
-        free( psz_query );
-    }
-    else
-        files = smb_find( p_sys->p_session, p_sys->i_tid, "\\*" );
-
-    if( files == NULL )
-        return VLC_ENOITEM;
-
-    files_count = smb_stat_list_count( files );
-    for( size_t i = 0; i < files_count; i++ )
-    {
-        st = smb_stat_list_at( files, i );
-
-        if( st == NULL )
-            goto error;
-
-        psz_name = smb_stat_name( st );
-
-        /* Avoid infinite loop */
-        if( !strcmp( psz_name, ".") || !strcmp( psz_name, "..") )
-            continue;
-
-        AddToNode( p_access, p_node, psz_name );
-    }
-
-    smb_stat_list_destroy( files );
-    return VLC_SUCCESS;
-
-    error:
-        smb_stat_list_destroy( files );
-        return VLC_ENOITEM;
-}
-
-/*****************************************************************************
- * Control:
- *****************************************************************************/
-static int Control( access_t *p_access, int i_query, va_list args )
-{
-    VLC_UNUSED( p_access );
-
-    switch( i_query )
-    {
-        case ACCESS_CAN_SEEK:
-        case ACCESS_CAN_FASTSEEK:
-            *va_arg( args, bool* ) = false;
-            break;
-
-        case ACCESS_CAN_PAUSE:
-        case ACCESS_CAN_CONTROL_PACE:
-            *va_arg( args, bool* ) = true;
-            break;
-
-        case ACCESS_GET_PTS_DELAY:
-            *va_arg( args, int64_t * ) = DEFAULT_PTS_DELAY * 1000;
-            break;
-
-        default:
-            return VLC_EGENERIC;
-     }
-     return VLC_SUCCESS;
- }
-
-static bool AddToNode( access_t *p_access, input_item_node_t *p_node,
-                       const char *psz_name )
-{
-    access_sys_t *p_sys = p_access->p_sys;
-    input_item_t *p_item;
-    char         *psz_uri, *psz_option;
-    int           i_ret;
-
-    i_ret = asprintf( &psz_uri, "%s/%s", p_node->p_item->psz_uri, psz_name );
-    /* XXX Handle ENOMEM by enabling retry */
-    if( i_ret == -1 )
-        return false;
-
-    p_item = input_item_New( psz_uri, psz_name );
-    free( psz_uri );
-    if( p_item == NULL )
-        return false;
-
-    input_item_CopyOptions( p_node->p_item, p_item );
-    input_item_node_AppendItem( p_node, p_item );
-
-    /* Here we save on the node the credentials that allowed us to login.
-     * That way the user isn't prompted more than once for credentials */
-    i_ret = asprintf( &psz_option, "smb-user=%s", p_sys->creds.login );
-    if( i_ret != -1 )
-        input_item_AddOption( p_item, psz_option, VLC_INPUT_OPTION_TRUSTED );
-    free( psz_option );
-    i_ret = asprintf( &psz_option, "smb-pwd=%s", p_sys->creds.password );
-    if( i_ret != -1 )
-        input_item_AddOption( p_item, psz_option, VLC_INPUT_OPTION_TRUSTED );
-    free( psz_option );
-    asprintf( &psz_option, "smb-domain=%s", p_sys->creds.domain );
-    if( i_ret != -1 )
-        input_item_AddOption( p_item, psz_option, VLC_INPUT_OPTION_TRUSTED );
-    free( psz_option );
-
-    input_item_Release( p_item );
-    return true;
-}
diff --git a/modules/access/dsm/common.c b/modules/access/dsm/common.c
new file mode 100644
index 0000000..2b566d4
--- /dev/null
+++ b/modules/access/dsm/common.c
@@ -0,0 +1,483 @@
+/*****************************************************************************
+ * bdsm/common.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
+
+#include "common.h"
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+static void cleanup( bdsm_sys_t *p_sys );
+static bool get_path( bdsm_sys_t *p_sys );
+static int get_address(  bdsm_sys_t *p_sys );
+static void get_credentials( bdsm_sys_t *p_sys );
+static int login( bdsm_sys_t *p_sys, bdsm_login_cb_t *p_login_cb );
+static int add_item( bdsm_sys_t *p_sys, bdsm_item_cb_t *p_cb,
+                    const char *psz_parent_uri, const char *psz_name );
+
+/* get ip address/path/creds, then connects and try to authenticate */
+int bdsm_Init( bdsm_sys_t *p_sys, vlc_object_t *p_obj )
+{
+    memset( p_sys, 0, sizeof(bdsm_sys_t) );
+
+    p_sys->p_obj = p_obj;
+
+    p_sys->p_ns = netbios_ns_new();
+    if( p_sys->p_ns == NULL )
+        return VLC_EGENERIC;
+
+    return VLC_SUCCESS;
+}
+
+int bdsm_Connect( bdsm_sys_t *p_sys, bdsm_login_cb_t *p_login_cb,
+                  const char *psz_uri )
+{
+    smb_stat st;
+
+    cleanup( p_sys );
+
+    p_sys->p_session = smb_session_new();
+    if( p_sys->p_session == NULL )
+        goto error;
+
+    vlc_UrlParse( &p_sys->url, psz_uri, 0 );
+    get_credentials( p_sys );
+    if( get_address( p_sys ) != VLC_SUCCESS )
+        goto error;
+
+    msg_Dbg( p_sys->p_obj, "Creds: username = %s, password = %s, domain = %s",
+             p_sys->creds.login, p_sys->creds.password,
+             p_sys->creds.domain );
+    msg_Dbg( p_sys->p_obj, "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 */
+    if( !smb_session_connect( p_sys->p_session, p_sys->netbios_name,
+                              p_sys->addr.s_addr, SMB_TRANSPORT_TCP ) )
+    {
+        msg_Err( p_sys->p_obj, "Unable to connect/negotiate SMB session");
+        goto error;
+    }
+
+    if( login( p_sys, p_login_cb ) != VLC_SUCCESS )
+        goto error;
+
+    if( !get_path( p_sys ) )
+    {
+        p_sys->b_is_browsing = true;
+        return VLC_SUCCESS;
+    }
+
+    msg_Dbg( p_sys->p_obj, "Path: Share name = %s, path = %s", p_sys->psz_share,
+             p_sys->psz_path );
+
+    /* Connect to the share */
+    p_sys->i_tid = smb_tree_connect( p_sys->p_session, p_sys->psz_share );
+    if( !p_sys->i_tid )
+    {
+        msg_Err( p_sys->p_obj, "Unable to connect to share %s", p_sys->psz_share );
+        goto error;
+    }
+
+    /* Let's finally ask a handle to the file we wanna read ! */
+    p_sys->i_fd = smb_fopen( p_sys->p_session, p_sys->i_tid, p_sys->psz_path,
+                             SMB_MOD_RO );
+    if( !p_sys->i_fd )
+    {
+        msg_Err( p_sys->p_obj, "Unable to open file with path %s (in share %s)",
+                 p_sys->psz_path, p_sys->psz_share );
+        goto error;
+    }
+
+    st = smb_stat_fd( p_sys->p_session, p_sys->i_fd );
+    if( smb_stat_get( st, SMB_STAT_ISDIR ) )
+    {
+        smb_fclose( p_sys->p_session, p_sys->i_fd );
+        p_sys->b_is_browsing = true;
+    }
+    return VLC_SUCCESS;
+    error:
+        cleanup( p_sys );
+        return VLC_EGENERIC;
+}
+
+/* cleanup libdsm stuff */
+void bdsm_Destroy( bdsm_sys_t *p_sys )
+{
+    cleanup( p_sys );
+    if( p_sys->p_ns )
+        netbios_ns_destroy( p_sys->p_ns );
+}
+
+/* Split DOMAIN\User if it finds a '\' in psz_login. */
+void bdsm_SplitDomainLogin( char **psz_login, char **psz_domain )
+{
+    char *user = strchr( *psz_login, '\\' );
+
+    if( user != NULL )
+    {
+        *psz_domain = *psz_login;
+        *user = '\0';
+        *psz_login = strdup( user + 1 );
+    }
+}
+
+int bdsm_BrowseShare( bdsm_sys_t *p_sys, bdsm_item_cb_t *p_cb,
+                      const char *psz_uri )
+{
+    smb_share_list  shares;
+    const char     *psz_name;
+    size_t          share_count;
+    int             i_ret;
+
+    share_count = smb_share_get_list( p_sys->p_session, &shares );
+    if( !share_count )
+        return VLC_ENOITEM;
+
+    for( size_t i = 0; i < share_count; i++ )
+    {
+        psz_name = smb_share_list_at( shares, i );
+
+        if( psz_name[strlen( psz_name ) - 1] == '$')
+            continue;
+
+        i_ret = add_item( p_sys, p_cb, psz_uri, psz_name );
+        if( i_ret != VLC_SUCCESS )
+            goto error;
+    }
+
+    smb_share_list_destroy( shares );
+    return VLC_SUCCESS;
+    error:
+        smb_share_list_destroy( shares );
+        return i_ret;
+}
+
+int bdsm_BrowseDirectory( bdsm_sys_t *p_sys, bdsm_item_cb_t *p_cb,
+                          const char *psz_uri )
+{
+    smb_stat_list   files;
+    smb_stat        st;
+    char           *psz_query;
+    const char     *psz_name;
+    size_t          files_count;
+    int             i_ret;
+
+    if( p_sys->psz_path != NULL )
+    {
+        i_ret = asprintf( &psz_query, "%s\\*", p_sys->psz_path );
+        if( i_ret == -1 )
+            return VLC_ENOMEM;
+        files = smb_find( p_sys->p_session, p_sys->i_tid, psz_query );
+        free( psz_query );
+    }
+    else
+        files = smb_find( p_sys->p_session, p_sys->i_tid, "\\*" );
+
+    if( files == NULL )
+        return VLC_ENOITEM;
+
+    files_count = smb_stat_list_count( files );
+    for( size_t i = 0; i < files_count; i++ )
+    {
+        st = smb_stat_list_at( files, i );
+
+        if( st == NULL ) {
+            i_ret = VLC_ENOITEM;
+            goto error;
+        }
+
+        psz_name = smb_stat_name( st );
+
+        /* Avoid infinite loop */
+        if( !strcmp( psz_name, ".") || !strcmp( psz_name, "..") )
+            continue;
+
+        i_ret = add_item( p_sys, p_cb, psz_uri, psz_name );
+        if( i_ret != VLC_SUCCESS )
+            goto error;
+    }
+
+    smb_stat_list_destroy( files );
+    return VLC_SUCCESS;
+
+    error:
+        smb_stat_list_destroy( files );
+        return i_ret;
+}
+
+/*****************************************************************************
+ * Local functions
+ *****************************************************************************/
+
+static void cleanup( bdsm_sys_t *p_sys )
+{
+    p_sys->b_is_browsing = false;
+
+    if( p_sys->i_fd )
+        smb_fclose( p_sys->p_session, p_sys->i_fd );
+    p_sys->i_fd = 0;
+
+    if( p_sys->p_session )
+        smb_session_destroy( p_sys->p_session );
+    p_sys->p_session = NULL;
+    p_sys->i_tid = 0;
+
+    free( p_sys->creds.login );
+    p_sys->creds.login = NULL;
+
+    free( p_sys->creds.password );
+    p_sys->creds.password = NULL;
+
+    free( p_sys->creds.domain );
+    p_sys->creds.domain = NULL;
+
+    free( p_sys->url.psz_buffer );
+    p_sys->url.psz_buffer = NULL;
+
+    free( p_sys->psz_share );
+    p_sys->psz_share = NULL;
+
+    free( p_sys->psz_path );
+    p_sys->psz_path = NULL;
+}
+
+static void backslash_path( vlc_url_t *p_url )
+{
+    char *iter = p_url->psz_path;
+
+    /* 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) */
+static bool get_path( bdsm_sys_t *p_sys )
+{
+    char *iter;
+
+    if( p_sys->url.psz_path == NULL )
+        return false;
+
+    backslash_path( &p_sys->url );
+
+    /* Is path longer than just "/" ? */
+    if( strlen( p_sys->url.psz_path ) > 1 )
+    {
+        iter = p_sys->url.psz_path;
+        while( *iter == '\\' ) iter++; /* Handle smb://Host/////Share/ */
+
+        p_sys->psz_share = strdup( iter );
+        if ( p_sys->psz_share == NULL )
+            return false;
+    }
+    else
+    {
+        msg_Dbg( p_sys->p_obj, "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';
+        p_sys->psz_path = strdup( "" );
+
+        msg_Dbg( p_sys->p_obj, "no file path provided, will switch to browser ");
+        return true;
+    }
+
+    p_sys->psz_path = strdup( iter + 1); /* Skip the first \ */
+    *iter = '\0';
+    if( p_sys->psz_path == NULL )
+        return false;
+
+    return true;
+}
+
+/* Returns false if it wasn't able to get an ip address to connect to */
+static int get_address( bdsm_sys_t *p_sys )
+{
+    if( !inet_aton( p_sys->url.psz_host, &p_sys->addr ) )
+    {
+        /* 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 ? */
+        if( netbios_ns_resolve( p_sys->p_ns, p_sys->url.psz_host, 0x20,
+                                &p_sys->addr.s_addr) )
+        {
+            strlcpy( p_sys->netbios_name, p_sys->url.psz_host, 16);
+            return VLC_SUCCESS;
+        }
+        /* 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;
+            }
+            freeaddrinfo( p_info );
+            if( p_info->ai_family != AF_INET )
+                return VLC_EGENERIC;
+        }
+        else
+            return VLC_EGENERIC;
+    }
+
+    /* 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_sys->p_obj, "Unable to get netbios name of %s",
+            p_sys->url.psz_host );
+        p_sys->netbios_name[0] = '\0';
+    }
+
+    /* If no domain was explicitly specified, let's use the machine name */
+    if( p_sys->creds.domain == NULL && p_sys->netbios_name[0] )
+        p_sys->creds.domain = strdup( p_sys->netbios_name );
+
+    return VLC_SUCCESS;
+}
+
+/* Get credentials from uri or variables. */
+static void get_credentials( bdsm_sys_t *p_sys )
+{
+    /* Fetch credentials, either from URI or from options if not provided */
+    if( p_sys->url.psz_password == NULL )
+        p_sys->creds.password = var_InheritString( p_sys->p_obj, "smb-pwd" );
+    else
+        p_sys->creds.password = strdup( p_sys->url.psz_password );
+
+    /* Here we support smb://DOMAIN\User:password@XXX, get user from options
+       or default to "Guest" as last resort */
+    if( p_sys->url.psz_username != NULL )
+    {
+        p_sys->creds.login = strdup( p_sys->url.psz_username );
+        bdsm_SplitDomainLogin( &p_sys->creds.login, &p_sys->creds.domain );
+    }
+    else
+    {
+        p_sys->creds.login = var_InheritString( p_sys->p_obj, "smb-user" );
+        if( p_sys->creds.login == NULL )
+            p_sys->creds.login = strdup( "Guest" );
+    }
+
+    if( p_sys->creds.domain == NULL )
+        p_sys->creds.domain = var_InheritString( p_sys->p_obj, "smb-domain" );
+}
+
+/* Performs login with existing credentials and ask the user for new ones on
+   failure */
+static int login( bdsm_sys_t *p_sys, bdsm_login_cb_t *p_login_cb )
+{
+    if( p_sys->creds.login == NULL )
+        p_sys->creds.login = strdup( "Guest" );
+    if( p_sys->creds.password == NULL )
+        p_sys->creds.password = strdup( "Guest" );
+    if( p_sys->creds.domain == NULL )
+        p_sys->creds.domain = strdup( "WORKGROUP" );
+
+    /* Try to authenticate on the remote machine */
+    smb_session_set_creds( p_sys->p_session, p_sys->creds.domain,
+                           p_sys->creds.login, p_sys->creds.password );
+    if( !smb_session_login( p_sys->p_session ) )
+    {
+        if( p_login_cb != NULL )
+        {
+            p_login_cb->pf_login( p_login_cb->p_ctx );
+            smb_session_set_creds( p_sys->p_session, p_sys->creds.domain,
+                                   p_sys->creds.login, p_sys->creds.password );
+            if( smb_session_login( p_sys->p_session ) )
+                return VLC_SUCCESS;
+        }
+
+        /* FIXME, Try to force netbios name as domain then WORKGROUP here */
+        msg_Err( p_sys->p_obj, "Unable to login with username = %s, password = %s, domain = %s",
+                   p_sys->creds.login, p_sys->creds.password,
+                   p_sys->creds.domain );
+        return VLC_EGENERIC;
+    }
+    else if( smb_session_is_guest( p_sys->p_session ) == 1 )
+        msg_Warn( p_sys->p_obj, "Login failure but you were logged in as a Guest");
+
+    return VLC_SUCCESS;
+}
+
+static int add_item( bdsm_sys_t *p_sys, bdsm_item_cb_t *p_cb,
+                    const char *psz_parent_uri, const char *psz_name )
+{
+    input_item_t *p_item;
+    char         *psz_uri, *psz_option;
+    int           i_ret;
+
+    i_ret = asprintf( &psz_uri, "%s/%s", psz_parent_uri, psz_name );
+    if( i_ret == -1 )
+        return VLC_ENOMEM;
+
+    p_item = input_item_New( psz_uri, psz_name );
+    free( psz_uri );
+    if( p_item == NULL )
+        return VLC_ENOMEM;
+
+    /* Here we save on the node the credentials that allowed us to login.
+     * That way the user isn't prompted more than once for credentials */
+    i_ret = asprintf( &psz_option, "smb-user=%s", p_sys->creds.login );
+    if( i_ret == -1 )
+        return VLC_ENOMEM;
+    input_item_AddOption( p_item, psz_option, VLC_INPUT_OPTION_TRUSTED );
+    free( psz_option );
+
+    i_ret = asprintf( &psz_option, "smb-pwd=%s", p_sys->creds.password );
+    if( i_ret == -1 )
+        return VLC_ENOMEM;
+    input_item_AddOption( p_item, psz_option, VLC_INPUT_OPTION_TRUSTED );
+    free( psz_option );
+
+    i_ret = asprintf( &psz_option, "smb-domain=%s", p_sys->creds.domain );
+    if( i_ret == -1 )
+        return VLC_ENOMEM;
+    input_item_AddOption( p_item, psz_option, VLC_INPUT_OPTION_TRUSTED );
+    free( psz_option );
+
+    i_ret = p_cb->pf_add_item( p_cb->p_ctx, p_item );
+
+    input_item_Release( p_item );
+    return i_ret;
+}
diff --git a/modules/access/dsm/common.h b/modules/access/dsm/common.h
index cae5ab7..9e14b6d 100644
--- a/modules/access/dsm/common.h
+++ b/modules/access/dsm/common.h
@@ -36,10 +36,9 @@ int SdOpen( vlc_object_t * );
 void SdClose( vlc_object_t * );
 int vlc_sd_probe_Open( vlc_object_t * );
 
-int BrowserInit( access_t *p_access );
-
 struct access_sys_t
 {
+    vlc_object_t       *p_obj;
     netbios_ns         *p_ns;               /**< Netbios name service */
     smb_session        *p_session;          /**< bdsm SMB Session object */
     smb_creds           creds;              /**< Credentials used to connect */
@@ -53,5 +52,27 @@ struct access_sys_t
 
     smb_fd              i_fd;               /**< SMB fd for the file we're reading */
     smb_tid             i_tid;              /**< SMB Tree ID we're connected to */
+    bool                b_is_browsing;
+};
+typedef struct access_sys_t bdsm_sys_t;
+
+typedef struct bdsm_item_cb_t bdsm_item_cb_t;
+struct bdsm_item_cb_t {
+    int ( *pf_add_item ) ( void *p_ctx, input_item_t * );
+    void *p_ctx;
 };
 
+typedef struct bdsm_login_cb_t bdsm_login_cb_t;
+struct bdsm_login_cb_t {
+    void ( *pf_login ) ( void *p_ctx );
+    void *p_ctx;
+};
+
+/* get ip address/path/creds, then connects and try to authenticate */
+int bdsm_Init( bdsm_sys_t *, vlc_object_t * );
+int bdsm_Connect( bdsm_sys_t *, bdsm_login_cb_t *, const char * );
+void bdsm_Destroy( bdsm_sys_t * );
+void bdsm_SplitDomainLogin( char **psz_login, char **psz_domain );
+
+int bdsm_BrowseShare( bdsm_sys_t *, bdsm_item_cb_t *, const char * );
+int bdsm_BrowseDirectory( bdsm_sys_t *, bdsm_item_cb_t *, const char * );
-- 
2.1.3





More information about the vlc-devel mailing list