/*
   Unix SMB/Netbios implementation.
   Version 1.9.
   SMB client generic functions
   Copyright (C) Andrew Tridgell              1994-2000
   Copyright (C) Luke Kenneth Casson Leighton 1996-2000

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/


#define NO_SYSLOG

#include "includes.h"
#include "rpc_parse.h"
#include "rpc_client.h"
#include "trans2.h"

extern int DEBUGLEVEL;
extern pstring global_myname;

struct ncacn_np_use
{
        struct ncacn_np *cli;
        uint32 num_users;
};

static struct ncacn_np_use **msrpcs = NULL;
static uint32 num_msrpcs = 0;

/****************************************************************************
terminate client connection
****************************************************************************/
static void ncacn_np_shutdown(struct ncacn_np *cli)
{
	struct ntuser_creds	usr;
	BOOL			closed;

        if (cli != NULL)
        {
                if (cli->smb != NULL)
                {
                        if (cli->smb->initialised)
                        {
                                /* cli_nt_session_close(cli->smb, cli->fnum); JERRY */
                                cli_nt_session_close(cli->smb);
                        }
			create_ntc_from_cli_state(&usr, cli->smb);
                        cli_net_use_del(cli->smb->desthost, &usr, False, &closed);
                }
        }
}

static BOOL ncacn_np_establish_connection(struct ncacn_np *cli,
                                   const char *srv_name,
                                   const struct ntuser_creds *ntc,
                                   const char *pipe_name,
                                   BOOL reuse)
{
        BOOL new_smb_conn;
        cli->smb = cli_net_use_add(srv_name, ntc,
                                   True, &new_smb_conn);
        if (cli->smb == NULL)
        {
                return False;
        }
        /* if (!cli_nt_session_open(cli->smb, pipe_name, &cli->fnum))  by JERRY */
        if (!cli_nt_session_open(cli->smb, (char *)pipe_name))
        {
                cli_net_use_del(srv_name, ntc, False, NULL);
                return False;
        }
        fstrcpy(cli->pipe_name, pipe_name);
        return True;
}



/****************************************************************************
terminate client connection
****************************************************************************/
static void ncacn_np_use_free(struct ncacn_np_use *cli)
{
        if (cli->cli != NULL)
        {
                if (cli->cli->initialised)
                {
                        ncacn_np_shutdown(cli->cli);
                }
                ZERO_STRUCTP(cli->cli);
                free(cli->cli);
        }
        ZERO_STRUCTP(cli);
        free(cli);
}

/****************************************************************************
add a client state to the array
****************************************************************************/
static struct ncacn_np_use *add_ncacn_np_to_array(uint32 * len,
                                                  struct ncacn_np_use
                                                  ***array,
                                                  struct ncacn_np_use *cli)
{
	
        int i;

	/* traverse the list and try to find a previously
	   allocate spot that is not being used */
        for (i = 0; i < num_msrpcs; i++)
        {
                if (msrpcs[i] == NULL)
                {
			/* found and empty spot to 
			   store the cli pointer */
                        msrpcs[i] = cli;
                        return cli;
                }
        }

        return (struct ncacn_np_use *)add_item_to_array(len,
                                                        (void ***)array,
                                                        (void *)cli);

}



/****************************************************************************
delete a client state
****************************************************************************/
BOOL ncacn_np_use_del(const char *srv_name, const char *pipe_name,
                      const vuser_key * key,
                      BOOL force_close, BOOL *connection_closed)
{
        int i;
        DEBUG(10, ("ncacn_np_net_use_del: %s. force close: %s ",
                   pipe_name, BOOLSTR(force_close)));
        if (key != NULL)
        {
                DEBUG(10, ("[%d,%x]", key->pid, key->vuid));
        }
        DEBUG(10, ("\n"));

        if (connection_closed != NULL)
        {
                *connection_closed = False;
        }

        if (strnequal("\\PIPE\\", pipe_name, 6))
        {
                pipe_name = &pipe_name[6];
        }

        if (strnequal("\\\\", srv_name, 2))
        {
                srv_name = &srv_name[2];
        }

        for (i = 0; i < num_msrpcs; i++)
        {
                char *ncacn_np_name = NULL;
                char *ncacn_np_srv_name = NULL;
                struct ncacn_np_use *c = msrpcs[i];
                vuser_key k;

                if (c == NULL || c->cli == NULL || c->cli->smb == NULL)
                        continue;

                ncacn_np_name = c->cli->pipe_name;
                ncacn_np_srv_name = c->cli->smb->desthost;

                k = c->cli->smb->key;

                DEBUG(10, ("use_del[%d]: %s %s %s %s [%d,%x]\n",
                           i, ncacn_np_name, ncacn_np_srv_name,
                           c->cli->smb->user_name,
                           c->cli->smb->domain, k.pid, k.vuid));

                if (strnequal("\\PIPE\\", ncacn_np_name, 6))
                {
                        ncacn_np_name = &ncacn_np_name[6];
                }
                if (!strequal(ncacn_np_name, pipe_name))
                {
                        continue;
                }
                if (strnequal("\\\\", ncacn_np_srv_name, 2))
                {
                        ncacn_np_srv_name = &ncacn_np_srv_name[2];
                }
                if (!strequal(ncacn_np_srv_name, srv_name))
                {
                        continue;
                }
                if (key->pid != k.pid || key->vuid != k.vuid)
                {
                        continue;
                }
                /* decrement number of users */
                c->num_users--;
                DEBUG(10, ("idx: %i num_users now: %d\n",
                           i, c->num_users));
                if (force_close || c->num_users == 0)
                {
                        ncacn_np_use_free(c);
                        msrpcs[i] = NULL;
                        if (connection_closed != NULL)
                        {
                                *connection_closed = True;
                        }
                }
                return True;
        }

        return False;
}

/****************************************************************************
find client state.  server name, user name, domain name and password must all
match.
****************************************************************************/
static struct ncacn_np_use *ncacn_np_find(const char *srv_name,
                                          const char *pipe_name,
                                          const vuser_key * key,
                                          const struct ntuser_creds
                                          *usr_creds, BOOL reuse)
{
        int i;
        const char *sv_name = srv_name;

        if (strnequal("\\PIPE\\", pipe_name, 6))
        {
                pipe_name = &pipe_name[6];
        }

        if (strnequal("\\\\", sv_name, 2))
        {
                sv_name = &sv_name[2];
        }

        if (usr_creds != NULL)
        {
                DEBUG(10, ("ncacn_np_find: %s %s %s",
                           srv_name, usr_creds->user_name, usr_creds->domain));
        }
        else
        {
                DEBUG(10,("ncacn_np_find: %s (no creds)\n", srv_name));
        }

        if (key != NULL)
        {
                DEBUG(10, ("[%d,%x]", key->pid, key->vuid));
        }
        DEBUG(10, ("\n"));

        for (i = 0; i < num_msrpcs; i++)
        {
                char *ncacn_np_srv_name = NULL;
                struct ncacn_np_use *c = msrpcs[i];
                vuser_key k;

                char *ncacn_np_name = NULL;

                if (c == NULL || c->cli == NULL || c->cli->smb == NULL ||
                    c->cli->smb->fd == -1 ||
                    !c->cli->initialised)
                {
                        continue;
                }

                ncacn_np_name = c->cli->pipe_name;
                ncacn_np_srv_name = c->cli->smb->desthost;

                k = c->cli->smb->key;

                DEBUG(10, ("ncacn_np_find[%d]: %s %s %s %s [%d,%x]\n",
                           i, ncacn_np_name, ncacn_np_srv_name,
                           c->cli->smb->user_name,
                           c->cli->smb->domain, k.pid, k.vuid));

                if (strnequal("\\\\", ncacn_np_srv_name, 2))
                {
                        ncacn_np_srv_name = &ncacn_np_srv_name[2];
                }

                if (strnequal("\\PIPE\\", ncacn_np_name, 6))
                {
                        ncacn_np_name = &ncacn_np_name[6];
                }

                if (!strequal(ncacn_np_name, pipe_name))
                {
                        continue;
                }
                if (!strequal(ncacn_np_srv_name, sv_name))
                {
                        continue;
                }
                if (key != NULL && (k.pid != key->pid || k.vuid != key->vuid))
                {
                        continue;
                }
                if (usr_creds == NULL)
                {
                        if (reuse)
                        {
                                return c;
                        }
                        else
                        {
                                continue;
                        }
                }
                if (!strequal
                    (usr_creds->user_name, c->cli->smb->user_name))
                {
                        continue;
                }
                if (!reuse
                    && !pwd_compare((struct pwd_info *)&usr_creds->pwd, &c->cli->smb->pwd))
                {
                        DEBUG(100, ("password doesn't match\n"));
                        continue;
                }
                if (usr_creds->domain[0] == 0)
                {
                        return c;
                }
                if (strequal(usr_creds->domain, c->cli->smb->domain))
                {
                        return c;
                }
        }

        return NULL;
}


/****************************************************************************
initialise a msrpcent structure
****************************************************************************/
struct ncacn_np *ncacn_np_initialise(struct ncacn_np *msrpc,
                                     const vuser_key * key)
{
        if (!msrpc)
        {
                msrpc = (struct ncacn_np *)malloc(sizeof(*msrpc));
                if (!msrpc)
                        return NULL;
                ZERO_STRUCTP(msrpc);
        }

        if (msrpc->initialised)
        {
                ncacn_np_shutdown(msrpc);
        }

        ZERO_STRUCTP(msrpc);

        msrpc->fnum = -1;
        msrpc->initialised = 1;

        return msrpc;
}

/****************************************************************************
create a new client state from user credentials
****************************************************************************/
static struct ncacn_np_use *ncacn_np_use_get(const char *pipe_name,
                                             const vuser_key * key)
{
        struct ncacn_np_use *cli =
                (struct ncacn_np_use *)malloc(sizeof(*cli));

        if (cli == NULL)
        {
                return NULL;
        }

        memset(cli, 0, sizeof(*cli));

        cli->cli = ncacn_np_initialise(NULL, key);

        if (cli->cli == NULL)
        {
                return NULL;
        }

        return cli;
}

/****************************************************************************
init client state
****************************************************************************/
struct ncacn_np *ncacn_np_use_add(const char *pipe_name,
                                  const vuser_key * key,
                                  const char *srv_name,
                                  const struct ntuser_creds *ntc,
                                  BOOL reuse, BOOL *is_new_connection)
{
        struct ncacn_np_use *cli;
        DEBUG(10, ("ncacn_np_use_add: %s\n", pipe_name));

        (*is_new_connection) = False;
        cli = ncacn_np_find(srv_name, pipe_name, key, ntc, reuse);

        if (cli != NULL)
        {
                cli->num_users++;
                return cli->cli;
        }

        /*
         * allocate
         */

        (*is_new_connection) = True;

        cli = ncacn_np_use_get(pipe_name, key);

        if (!ncacn_np_establish_connection
            (cli->cli, srv_name, ntc, pipe_name, True))
        {
                DEBUG(0, ("ncacn_np_use_add: connection failed\n"));
                cli->cli = NULL;
                ncacn_np_use_free(cli);
                return NULL;
        }

        if (key != NULL)
        {
                cli->cli->smb->key = *key;
        }
        else
        {
                cli->cli->smb->key.pid = sys_getpid();
                cli->cli->smb->key.vuid = UID_FIELD_INVALID;
        }

        add_ncacn_np_to_array(&num_msrpcs, &msrpcs, cli);
        cli->num_users++;
        return cli->cli;
}



