#include "includes.h"

void t_init(char const *argv0)
{
    char *debuglevel;

    if ((debuglevel = getenv("DEBUGLEVEL")) != NULL) {
	DEBUGLEVEL = atoi(debuglevel);
    }

    lp_load(dyn_CONFIGFILE,False,False,False);

    setup_logging(argv0, True);
}

#define PROTOCOL_DEFAULT PROTOCOL_NT1
#define WORKGROUP_DEFAULT "TEST-WG"
#define CLIENT_DEFAULT "TEST-CLIENT"

struct cli_state *t_connect(
    int protocol_c,
    char const *server,
    char const *share,
    char const *workgroup_c,
    char const *client_c,
    char const *user,
    char const *password
) {
    int protocol = protocol_c ? protocol_c : PROTOCOL_DEFAULT;
    char const *workgroup = workgroup_c ? workgroup_c : WORKGROUP_DEFAULT;
    char const *client = client_c ? client_c : CLIENT_DEFAULT;
    struct cli_state *c;
    struct nmb_name server_nbn, client_nbn;
    struct in_addr ip;

    DEBUG(4, ("t_connect: protocol: %d\n", protocol));
    DEBUG(4, ("t_connect: server: %s\n", server));
    DEBUG(4, ("t_connect: share: %s\n", share));
    DEBUG(4, ("t_connect: workgroup: %s\n", workgroup));
    DEBUG(4, ("t_connect: user: %s\n", user));
    DEBUG(10, ("t_connect: password %s\n", password));
    make_nmb_name(&client_nbn, client, 0x0);
    make_nmb_name(&server_nbn , server, 0x20);

again:
    zero_ip(&ip);

    if (!(c=cli_initialise(NULL)) || !cli_connect(c, server, &ip)) {
	DEBUG(0, ("Connection to %s failed\n", server));
	return NULL;
    }
    c->protocol = protocol;

    if (!cli_session_request(c, &client_nbn, &server_nbn)) {
	    DEBUG(0, ("Session request to %s failed\n", server_nbn.name));
	    cli_shutdown(c);
	    if (strcmp(server_nbn.name, "*SMBSERVER")) {
		make_nmb_name(&server_nbn , "*SMBSERVER", 0x20);
		goto again;
	    }
	    return NULL;
    }

    DEBUG(4, ("t_connect: session request ok\n"));

    if (!cli_negprot(c)) {
	DEBUG(0, ("Protocol negotiation failed\n"));
	cli_shutdown(c);
	return NULL;
    }

    if (!cli_session_setup(c, user,
			password, strlen(password),
			password, strlen(password),
			workgroup)) {
	DEBUG(0, ("Session setup failed: %s\n", cli_errstr(c)));
	return NULL;
    }

    if (*c->server_domain || *c->server_os || *c->server_type)
	DEBUG(4, ("t_connect: server domain: %s\n", c->server_domain));
	DEBUG(4, ("t_connect: server os: %s\n", c->server_os));
	DEBUG(4, ("t_connect: server type: %s\n", c->server_type));

    DEBUG(4, ("t_connect: session setup ok\n"));

    if (!cli_send_tconX(c, share, "?????", password, strlen(password)+1)) {
	DEBUG(0,("Tree connect failed: %s\n", cli_errstr(c)));
	cli_shutdown(c);
	return NULL;
    }

    DEBUG(4, ("t_connect: tconX ok\n"));

    return c;
}

int t_file_exist(struct cli_state *cli, char const *file)
{
    return cli_getatr(cli, file, NULL, NULL, NULL);
}

int main(int argc, char *argv[])
{
    char *server = argv[1];
    char *share = argv[2];
    char *user = argv[3];
    char *password = argv[4];

    struct cli_state *cli1, *cli2;
    int fnum1, fnum2;
    char *file_org = "test.txt";
    char *file_tmp_old = "test.old";
    char *file_tmp_new = "test.new";

    t_init(argv[0]);

    cli1 = t_connect(0, server, share, NULL, NULL, user, password);
    cli2 = t_connect(0, server, share, NULL, NULL, user, password);
    if (!cli1 || !cli2) {
	DEBUG(0, ("Failed to connect to share: //%s/%s\n", server, share));
	exit(1);
    }

    /*
	prepare: create file.xls
	prepare: remove file.old and file.new
	client1: open file.xls in read/write mode
	client2: open file.xls in read-only mode
	client1: create file.new
	client1: rename file.xls to file.old
	client1: rename file.new to file.xls
	client1: set delete_on_close to file.old
	client1: close file.old
	    file.old is NOT deleted here.
	client2: close file.old (old file.xls)
	    file.old is deleted now.
	    file.xls is NOT deleted.
    */

    /* Prepare: Create 'file.xls' */
    fnum1 = cli_nt_create_full(cli1, file_org,
	/* CreateFlags */
	0,
	/* DesiredAccess*/
	GENERIC_ALL_ACCESS,
	/* FileAttributes */
	FILE_ATTRIBUTE_NORMAL,
	/* ShareAccess */
	0,
	/* CreateDisposition */
	FILE_OVERWRITE_IF,
	/* CreateOptions */
	//FILE_DELETE_ON_CLOSE,
	0,
	/* SecurityFlags */
	0
    );
    if (fnum1 == -1) {
	printf("Create failed: prepare: %s: %s\n", file_org, cli_errstr(cli1));
	exit(1);
    }
    cli_close(cli1, fnum1);

    /* Prepare: Remove 'bar' if exists */
    if (t_file_exist(cli1, file_tmp_old) && !cli_unlink(cli1, file_tmp_old)) {
	printf("Unlink failed: prepare: %s: %s\n", file_tmp_old, cli_errstr(cli1));
	exit(1);
    }
    if (t_file_exist(cli1, file_tmp_new) && !cli_unlink(cli1, file_tmp_new)) {
	printf("Unlink failed: prepare: %s: %s\n", file_tmp_new, cli_errstr(cli1));
	exit(1);
    }

#if 0
    /* Client1: Open 'file.xls' in write mode */
    fnum1 = cli_nt_create_full(cli1, file_org,
	/* CreateFlags */
	0,
	/* DesiredAccess*/
	GENERIC_ALL_ACCESS,
	/* FileAttributes */
	FILE_ATTRIBUTE_NORMAL,
	/* ShareAccess */
	FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
	/* CreateDisposition */
	FILE_OPEN,
	/* CreateOptions */
	0,
	/* SecurityFlags */
	0
    );
    if (fnum1 == -1) {
	printf("Open failed: client1: %s: %s\n", file_org, cli_errstr(cli1));
	exit(1);
    }
#endif

    /* Client2: Open 'file.xls' in read mode */
    fnum2 = cli_nt_create_full(cli2, file_org,
	/* CreateFlags */
	0,
	/* DesiredAccess*/
	GENERIC_READ_ACCESS,
	/* FileAttributes */
	FILE_ATTRIBUTE_NORMAL,
	/* ShareAccess */
	FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
	/* CreateDisposition */
	FILE_OPEN,
	/* CreateOptions */
	0,
	/* SecurityFlags */
	0
    );
    if (fnum2 == -1) {
	printf("Open failed: client2: %s: %s\n", file_org, cli_errstr(cli2));
	exit(1);
    }

    /* Client1: Create 'file.new' */
    fnum1 = cli_nt_create_full(cli1, file_tmp_new,
	/* CreateFlags */
	0,
	/* DesiredAccess*/
	GENERIC_ALL_ACCESS,
	/* FileAttributes */
	FILE_ATTRIBUTE_NORMAL,
	/* ShareAccess */
	FILE_SHARE_READ|FILE_SHARE_WRITE,
	/* CreateDisposition */
	FILE_CREATE,
	/* CreateOptions */
	0,
	/* SecurityFlags */
	0
    );
    if (fnum1 == -1) {
	printf("Create failed: client1: %s: %s\n", file_tmp_new, cli_errstr(cli1));
	exit(1);
    }
    cli_close(cli1, fnum1);

    /* Client1: Rename 'file.xls' to 'file.old' */
    if (!cli_rename(cli1, file_org, file_tmp_old)) {
	printf("Rename failed: client1: %s: %s: %s\n", file_org, file_tmp_old, cli_errstr(cli1));
	exit(1);
    }
    /* Client1: Rename 'file.new' to 'file.xls' */
    if (!cli_rename(cli1, file_tmp_new, file_org)) {
	printf("Rename failed: client1: %s: %s: %s\n", file_tmp_old, file_org, cli_errstr(cli1));
	exit(1);
    }
    // FIXME
    sleep(10);

#if 1
    /* Client1: Open 'file.xls' in write mode */
    fnum1 = cli_nt_create_full(cli1, file_tmp_old,
	/* CreateFlags */
	0,
	/* DesiredAccess*/
	GENERIC_ALL_ACCESS,
	/* FileAttributes */
	FILE_ATTRIBUTE_NORMAL,
	/* ShareAccess */
	FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
	/* CreateDisposition */
	FILE_OPEN,
	/* CreateOptions */
	0,
	/* SecurityFlags */
	0
    );
    if (fnum1 == -1) {
	printf("Open failed: client1: %s: %s\n", file_tmp_old, cli_errstr(cli1));
	exit(1);
    }
#endif

    /* Client1: Set DELETE_ON_CLOSE to 'file.old' (old 'file.xls') */
    if (!cli_nt_delete_on_close(cli1, fnum1, True)) {
	printf("Set DELETE_ON_CLOSE failed: client1: %s: %s\n", file_tmp_old, cli_errstr(cli1));
	exit(1);
    }
    /* Client1: Close 'fild.old' (old 'fild.old') */
    cli_close(cli1, fnum1);
    // FIXME
    //sleep(10);

    /* Check if 'file.xls' is NOT deleted */
    if (!t_file_exist(cli1, file_org)) {
	printf("a: %s: %s\n", file_org, cli_errstr(cli1));
	exit(1);
    }
    /* Check if 'file.old' is NOT deleted */
#if 1 /* NT_STATUS_DELETE_PENDING */
    if (!t_file_exist(cli1, file_tmp_old)) {
	printf("b: %s: %s\n", file_org, cli_errstr(cli1));
//	exit(1);
    }
#endif

    /* Client1: Close 'file.old' (old 'file.xls') */
    cli_close(cli2, fnum2);

    /* Check if 'file.xls' is NOT deleted */
    if (!t_file_exist(cli1, file_org)) {
	printf("c: %s: %s\n", file_org, cli_errstr(cli1));
	exit(1);
    }
    /* Check if 'file.old' is deleted */
    if (t_file_exist(cli1, file_tmp_old)) {
	printf("d: %s: %s\n", file_org, cli_errstr(cli1));
	exit(1);
    }

    exit(0);
}

