<commit_before>/*****************************************************************************
 * bdsm/access.c: liBDSM based SMB/CIFS access module
 *****************************************************************************
 * Copyright (C) 2001-2014 VLC authors and VideoLAN
 *
 * Authors: <NAME>' 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 <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_services_discovery.h>
#include <vlc_url.h>
#include <vlc_access.h>
#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>

#include <bdsm/bdsm.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
int bdsm_SdOpen( vlc_object_t * );
void bdsm_SdClose( vlc_object_t * );
int bdsm_sd_probe_Open( vlc_object_t * );

static int Open( vlc_object_t * );
static void Close( vlc_object_t * );

#define vlc_sd_probe_Open bdsm_sd_probe_Open

#define USER_TEXT N_("SMB user name")
#define USER_LONGTEXT N_("User name that will " \
    "be used for the connection. username in uri take precedence over this")
#define PASS_TEXT N_("SMB password")
#define PASS_LONGTEXT N_("Password that will be " \
    "used for the connection. Password in uri take precedence over this.")
#define DOMAIN_TEXT N_("SMB domain")
#define DOMAIN_LONGTEXT N_("Domain/Workgroup that " \
    "will be used for the connection. Domain of uri will also be tried.")

#define BDSM_LOGIN_DIALOG_RETRY 1

#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 )
    add_string( "smb-user", NULL, USER_TEXT, USER_LONGTEXT, false )
    add_password( "smb-pwd", NULL, PASS_TEXT, PASS_LONGTEXT, false )
    add_string( "smb-domain", NULL, DOMAIN_TEXT, DOMAIN_LONGTEXT, false )
    add_shortcut( "smb", "cifs" )
    set_callbacks( Open, Close )

    add_submodule()
        set_category( CAT_PLAYLIST )
        set_subcategory( SUBCAT_PLAYLIST_SD )
        set_capability( "services_discovery", 0 )
        set_callbacks( bdsm_SdOpen, bdsm_SdClose )

        VLC_SD_PROBE_SUBMODULE

vlc_module_end ()

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
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 int BrowserInit( access_t *p_access );

static void split_domain_login( char **psz_login, char **psz_domain );
static void get_credentials( access_t *p_access );
static int get_address( access_t *p_access );
static void login_dialog( access_t *p_access );
static int login( access_t *p_access );
static void backslash_path( vlc_url_t *p_url );
static bool get_path( access_t *p_access );
static int add_item( access_t *p_access,  input_item_node_t *p_node,
                     const char *psz_name );

struct access_sys_t
{
    netbios_ns         *p_ns;               /**< Netbios name service */
    smb_session        *p_session;          /**< bdsm SMB Session object */
    smb_creds           creds;              /**< Credentials used to connect */

    vlc_url_t           url;
    char               *psz_share;
    char               *psz_path;

    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 */
    bool                b_is_browsing;
};

/*****************************************************************************
 * Dialog strings
 *****************************************************************************/
#define BDSM_LOGIN_DIALOG_TITLE N_( "%s: Authentication required" )
#define BDSM_LOGIN_DIALOG_TEXT N_( "The computer you are trying to connect "   \
    "to requires authentication.\n Please provide a username (and ideally a "  \
    "domain name using the format DOMAIN\\username)\n and a password." )

/*****************************************************************************
 * Open: Initialize module's data structures and libdsm
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys;
    smb_stat st;

    /* Init p_access */
    access_InitFields( 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;

    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;

    vlc_UrlParse( &p_sys->url, p_access->psz_location, 0 );
    get_credentials( p_access );
    if( get_address( p_access ) != VLC_SUCCESS )
        goto error;

    msg_Dbg( p_access, "Creds: username = %s, domain = %s",
             p_sys->creds.login, 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");
        goto error;
    }

    if( login( p_access ) != VLC_SUCCESS )
        goto error;

    if( !get_path( p_access ) )
    {
        p_sys->b_is_browsing = true;
        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 );
        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 );
        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 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:
        Close( p_this );
        return VLC_EGENERIC;
}

/*****************************************************************************
 * 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;

    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 );
    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 );
    free( p_sys );
}

/*****************************************************************************
 * 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 = p_access->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 VLC_EGENERIC if it wasn't able to get an ip address to connect to */
static int get_address( access_t *p_access )
{
    access_sys_t *p_sys = p_access->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,
                                NETBIOS_FILESERVER,
                                &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_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 VLC_SUCCESS;
}

/* Displays a dialog for the user to enter his/her credentials */
static void login_dialog( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;

    char *psz_login = NULL, *psz_pass = NULL, *psz_title;
    int i_ret;

    i_ret = asprintf( &psz_title, BDSM_LOGIN_DIALOG_TITLE, p_sys->netbios_name );
    if( i_ret != -1 )
        dialog_Login( p_access, &psz_login, &psz_pass, psz_title,
                      BDSM_LOGIN_DIALOG_TEXT );
    else
        dialog_Login( p_access, &psz_login, &psz_pass, BDSM_LOGIN_DIALOG_TITLE,
                      BDSM_LOGIN_DIALOG_TEXT );
    free( psz_title );

    if( psz_login != NULL )
    {
        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 );
    }

    if( psz_pass != NULL )
    {
        if( p_sys->creds.password != NULL )
            free( p_sys->creds.password );
        p_sys->creds.password = <PASSWORD>;
    }
}

/* Performs login with existing credentials and ask the user for new ones on
   failure */
static int login( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;

    if( p_sys->creds.login == NULL )
        p_sys->creds.login = strdup( "Guest" );
    if( p_sys->creds.password == NULL )
        p_sys->creds.password = strdup( "<PASSWORD>" );
    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 );
            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_access, "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_access, "Login failure but you were logged in as a Guest");

    return VLC_SUCCESS;
}
<commit_msg>dsm: don't log password on error<commit_after>/*****************************************************************************
 * bdsm/access.c: liBDSM based SMB/CIFS access module
 *****************************************************************************
 * Copyright (C) 2001-2014 VLC authors and VideoLAN
 *
 * Authors: <NAME> <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 <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_services_discovery.h>
#include <vlc_url.h>
#include <vlc_access.h>
#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>

#include <bdsm/bdsm.h>

/*****************************************************************************
 * Module descriptor
 *****************************************************************************/
int bdsm_SdOpen( vlc_object_t * );
void bdsm_SdClose( vlc_object_t * );
int bdsm_sd_probe_Open( vlc_object_t * );

static int Open( vlc_object_t * );
static void Close( vlc_object_t * );

#define vlc_sd_probe_Open bdsm_sd_probe_Open

#define USER_TEXT N_("SMB user name")
#define USER_LONGTEXT N_("User name that will " \
    "be used for the connection. username in uri take precedence over this")
#define PASS_TEXT N_("SMB password")
#define PASS_LONGTEXT N_("Password that will be " \
    "used for the connection. Password in uri take precedence over this.")
#define DOMAIN_TEXT N_("SMB domain")
#define DOMAIN_LONGTEXT N_("Domain/Workgroup that " \
    "will be used for the connection. Domain of uri will also be tried.")

#define BDSM_LOGIN_DIALOG_RETRY 1

#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 )
    add_string( "smb-user", NULL, USER_TEXT, USER_LONGTEXT, false )
    add_password( "smb-pwd", NULL, PASS_TEXT, PASS_LONGTEXT, false )
    add_string( "smb-domain", NULL, DOMAIN_TEXT, DOMAIN_LONGTEXT, false )
    add_shortcut( "smb", "cifs" )
    set_callbacks( Open, Close )

    add_submodule()
        set_category( CAT_PLAYLIST )
        set_subcategory( SUBCAT_PLAYLIST_SD )
        set_capability( "services_discovery", 0 )
        set_callbacks( bdsm_SdOpen, bdsm_SdClose )

        VLC_SD_PROBE_SUBMODULE

vlc_module_end ()

/*****************************************************************************
 * Local prototypes
 *****************************************************************************/
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 int BrowserInit( access_t *p_access );

static void split_domain_login( char **psz_login, char **psz_domain );
static void get_credentials( access_t *p_access );
static int get_address( access_t *p_access );
static void login_dialog( access_t *p_access );
static int login( access_t *p_access );
static void backslash_path( vlc_url_t *p_url );
static bool get_path( access_t *p_access );
static int add_item( access_t *p_access,  input_item_node_t *p_node,
                     const char *psz_name );

struct access_sys_t
{
    netbios_ns         *p_ns;               /**< Netbios name service */
    smb_session        *p_session;          /**< bdsm SMB Session object */
    smb_creds           creds;              /**< Credentials used to connect */

    vlc_url_t           url;
    char               *psz_share;
    char               *psz_path;

    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 */
    bool                b_is_browsing;
};

/*****************************************************************************
 * Dialog strings
 *****************************************************************************/
#define BDSM_LOGIN_DIALOG_TITLE N_( "%s: Authentication required" )
#define BDSM_LOGIN_DIALOG_TEXT N_( "The computer you are trying to connect "   \
    "to requires authentication.\n Please provide a username (and ideally a "  \
    "domain name using the format DOMAIN\\username)\n and a password." )

/*****************************************************************************
 * Open: Initialize module's data structures and libdsm
 *****************************************************************************/
static int Open( vlc_object_t *p_this )
{
    access_t     *p_access = (access_t*)p_this;
    access_sys_t *p_sys;
    smb_stat st;

    /* Init p_access */
    access_InitFields( 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;

    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;

    vlc_UrlParse( &p_sys->url, p_access->psz_location, 0 );
    get_credentials( p_access );
    if( get_address( p_access ) != VLC_SUCCESS )
        goto error;

    msg_Dbg( p_access, "Creds: username = %s, domain = %s",
             p_sys->creds.login, 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");
        goto error;
    }

    if( login( p_access ) != VLC_SUCCESS )
        goto error;

    if( !get_path( p_access ) )
    {
        p_sys->b_is_browsing = true;
        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 );
        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 );
        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 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:
        Close( p_this );
        return VLC_EGENERIC;
}

/*****************************************************************************
 * 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;

    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 );
    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 );
    free( p_sys );
}

/*****************************************************************************
 * 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 = p_access->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 VLC_EGENERIC if it wasn't able to get an ip address to connect to */
static int get_address( access_t *p_access )
{
    access_sys_t *p_sys = p_access->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,
                                NETBIOS_FILESERVER,
                                &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_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 VLC_SUCCESS;
}

/* Displays a dialog for the user to enter his/her credentials */
static void login_dialog( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;

    char *psz_login = NULL, *psz_pass = NULL, *psz_title;
    int i_ret;

    i_ret = asprintf( &psz_title, BDSM_LOGIN_DIALOG_TITLE, p_sys->netbios_name );
    if( i_ret != -1 )
        dialog_Login( p_access, &psz_login, &psz_pass, psz_title,
                      BDSM_LOGIN_DIALOG_TEXT );
    else
        dialog_Login( p_access, &psz_login, &psz_pass, BDSM_LOGIN_DIALOG_TITLE,
                      BDSM_LOGIN_DIALOG_TEXT );
    free( psz_title );

    if( psz_login != NULL )
    {
        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 );
    }

    if( psz_pass != NULL )
    {
        if( p_sys->creds.password != NULL )
            free( p_sys->creds.password );
        p_sys->creds.password = <PASSWORD>;
    }
}

/* Performs login with existing credentials and ask the user for new ones on
   failure */
static int login( access_t *p_access )
{
    access_sys_t *p_sys = p_access->p_sys;

    if( p_sys->creds.login == NULL )
        p_sys->creds.login = strdup( "Guest" );
    if( p_sys->creds.password == NULL )
        p_sys->creds.password = strdup( "<PASSWORD>" );
    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 );
            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_access, "Unable to login with username = %s, domain = %s",
                   p_sys->creds.login, p_sys->creds.domain );
        return VLC_EGENERIC;
    }
    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");

    return VLC_SUCCESS;
}

{'fraction_non_alphanumeric': 0.19120996918129438, 'fraction_numerical': 0.0030148733753182365, 'mean_word_length': 2.8679709769370305, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 274, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '786641', 'n_tokens_mistral': 10048, 'n_tokens_neox': 9760, 'n_words': 2395}