/*
 * Copyright (c) 2002-2005 Sendmail, Inc. and its suppliers.
 *	All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 */

#include "sm/generic.h"
SM_IDSTR(id, "@(#)$Id: t-idb-0.c,v 1.59 2007/06/18 04:42:31 ca Exp $")

#include "sm/io.h"
#include "sm/mta.h"
#include "sm/cstr.h"
#include "sm/cdb.h"
#include "sm/ibdb.h"
#include "sm/qmgr.h"
#include "sm/memops.h"
#include "sm/bhtable.h"
#include "sm/sysexits.h"
#include "sm/test.h"
#include "ibdb.h"

#include <stdio.h>

/*
**  Test program to write and read IBDB.
**  Uses wribdb.c to write IBDB.
*/

#define FNAME	"ibd"
#define IBDBSIZE	8192
#define INIT_SEQ	1
static int init_seq = INIT_SEQ;
static uint n_trunc = 128;
static int Verbose = 0;

/*
**  ToDo:
**	- more consistency checks for read data.
**	- cleanup files.
*/

#include "../checks/wribdb.c"

/*
**  TESTIDBR -- read INCEDB records
**
**	Parameters:
**		base_dir -- name of base directory (can be NULL)
**		iter -- number of iterations (transactions)
**		maxs -- maximum size of an INCEDB file
**
**	Returns:
**		usual sm_error code.
*/

static sm_ret_T
testidbr(const char *base_dir, int iter, int maxs, uint flags)
{
	int i, j, nrcpts, status;
	ibdbr_ctx_P ibdbr_ctx;
	ibdb_ta_P ibdb_ta;
	ibdb_rcpt_P rcpt;
	ibdb_hdrmod_P ibdb_hdrmod;
	sessta_id_T ta_id;
	id_count_T id_count;
	sm_str_P str;
	sm_ret_T ret;
	uint32_t first, last;
	char hdr[SM_MAXHDRLEN];

	ibdb_ta = NULL;
	rcpt = NULL;
	str = NULL;
	ibdbr_ctx = NULL;
	last = 1;

	ret = ibdbf_get_seq(base_dir, FNAME, IBDB_OFL_WRITE, &first, &last);
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return ret;

	str = sm_str_new(NULL, 256, 256);
	SM_TEST(str != NULL);
	if (str == NULL)
		goto end;

	if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_TRUNCATE) &&
	    !WRIBDB_IS_FLAG(flags, WRIBDB_FL_TRUNCATED))
	{
		int r;
		struct stat sb;
		off_t size;
		const char *name;

		crt_ibdb_path(str, IBDB_WR_DIR, FNAME, last);
		name = (const char *) sm_str_getdata(str);
		r = stat(name, &sb);
		if (Verbose > 1)
			sm_io_fprintf(smioerr,
				"truncating=%s, r=%d, size=%ld\n",
				name, r, sb.st_size);
		SM_TEST(0 == r);
		if (0 == r && (size = sb.st_size) > 0)
		{
			if (size > n_trunc)
				size -= n_trunc;
			else
				--size;
			r = truncate(name, size);
		}
		flags |= WRIBDB_FL_TRUNCATED;
	}

	ret = ibdbr_open(base_dir, FNAME, SM_IO_RDONLY, init_seq, maxs,
			IBDB_OFL_WRITE, &ibdbr_ctx);
	if (sm_is_err(ret) && sm_error_value(ret) == ENOENT)
	{
		ret = ibdbr_open(base_dir, FNAME, SM_IO_RDONLY, first, maxs,
				IBDB_OFL_WRITE, &ibdbr_ctx);
		SM_TEST(ret == SM_SUCCESS);
		if (ret != SM_SUCCESS)
			return ret;
	}
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return ret;

	ibdb_ta = (ibdb_ta_P) sm_zalloc(sizeof(*ibdb_ta));
	SM_TEST(ibdb_ta != NULL);
	if (ibdb_ta == NULL)
		goto end;
	ibdb_ta->ibt_ta_id = ta_id;
	ibdb_ta->ibt_mail_pa = sm_str_new(NULL, 256, 256);
	SM_TEST(ibdb_ta->ibt_mail_pa != NULL);
	if (ibdb_ta->ibt_mail_pa == NULL)
		goto end;
	ibdb_hdrmod = (ibdb_hdrmod_P) sm_zalloc(sizeof(*ibdb_hdrmod));
	SM_TEST(ibdb_hdrmod != NULL);
	if (ibdb_hdrmod == NULL)
		goto end;
	ibdb_hdrmod->ibh_ta_id = ta_id;

	rcpt = (ibdb_rcpt_P) sm_zalloc(sizeof(*rcpt));
	SM_TEST(rcpt != NULL);
	if (rcpt == NULL)
		goto end;
	rcpt->ibr_ta_id = ta_id;
	rcpt->ibr_pa = sm_str_new(NULL, 256, 256);
	SM_TEST(rcpt->ibr_pa != NULL);
	if (rcpt->ibr_pa == NULL)
		goto end;

	id_count = 0;

	if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_1OPENTA))
	{
		i = INT_MAX;
		j = INT_MAX > 2;
		sm_str_clr(str);
		ret = sm_strprintf(str, "rcpt-%d-%d@other.Rcpt", i, j);
		ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod, &status);
		SM_TEST_E(ret == RT_IBDB_RCPT);
		SM_TEST_E(sm_memeq(sm_str_getdata(str),
			sm_str_getdata(rcpt->ibr_pa),
			sm_str_getlen(rcpt->ibr_pa)));
		SM_TEST_E(rcpt->ibr_idx == (rcpt_idx_T) j);
		ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod, &status);
		SM_TEST_E(ret == RT_IBDB_TA);
		SM_CSTR_FREE(ibdb_ta->ibt_cdb_id);
	}

	for (i = 0; i < iter; i++)
	{
		id_count++;

		nrcpts = (rcpts_per_ta > 0) ? rcpts_per_ta : (i % 5) + 1;
		for (j = 0; j < nrcpts; j++)
		{
			sm_str_clr(str);
			ret = sm_strprintf(str,
					"rcpt-%d-%d@other.Rcpt", i, j);

			ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod
					, &status);
			SM_TEST(ret == RT_IBDB_RCPT);
			if (sm_is_err(ret))
				goto end;
			SM_TEST(sm_memeq(sm_str_getdata(str),
				sm_str_getdata(rcpt->ibr_pa),
				sm_str_getlen(rcpt->ibr_pa)));
			SM_TEST(rcpt->ibr_idx == (rcpt_idx_T) j);
		}

		ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod, &status);

		if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_LONG))
		{
			SM_TEST_E(RT_IBDB_HDRMOD == ret);
			SM_TEST(SM_HM_TYPE_PREPEND == ibdb_hdrmod->ibh_type);
			SM_TEST_E(NULL != long_str);
			j = snprintf(long_str, long_str_len,
					"HeaderPreLong: prepend ta=%s i=%d",
					ta_id, i);
			for (; j < long_str_len - 4; j++)
				long_str[j] = '0' + (j % 10);
			long_str[long_str_len - 4] ='\r';
			long_str[long_str_len - 3] ='\n';
			long_str[long_str_len - 2] ='\0';
			SM_TEST(sm_memeq(long_str,
				sm_cstr_data(ibdb_hdrmod->ibh_hdr),
				sm_cstr_getlen(ibdb_hdrmod->ibh_hdr)));
			SM_CSTR_FREE(ibdb_hdrmod->ibh_hdr);

			ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod
					, &status);
		}

		if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_HDR_PRE))
		{
			SM_TEST(RT_IBDB_HDRMOD == ret);
			if (sm_is_err(ret))
				break;
			SM_TEST(SM_HM_TYPE_PREPEND == ibdb_hdrmod->ibh_type);
			ret = snprintf(hdr, sizeof(hdr),
					"HeaderPre: prepend ta=%s i=%d\r\n",
					ta_id, i);
			SM_TEST(sm_memeq(hdr,
				sm_cstr_data(ibdb_hdrmod->ibh_hdr),
				sm_cstr_getlen(ibdb_hdrmod->ibh_hdr)));
			SM_CSTR_FREE(ibdb_hdrmod->ibh_hdr);

			ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod
					, &status);
		}
		if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_HDR_REM))
		{
			SM_TEST(RT_IBDB_HDRMOD == ret);
			SM_TEST(SM_HM_TYPE_REMOVE == ibdb_hdrmod->ibh_type);
			SM_TEST(2 == ibdb_hdrmod->ibh_pos);
			SM_TEST(NULL == ibdb_hdrmod->ibh_hdr);
			ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod
					, &status);
		}
		if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_HDR_APP))
		{
			SM_TEST(RT_IBDB_HDRMOD == ret);
			SM_TEST(SM_HM_TYPE_APPEND == ibdb_hdrmod->ibh_type);
			ret = snprintf(hdr, sizeof(hdr),
					"HeaderApp: append ta=%s i=%d\r\n",
					ta_id, i);
			SM_TEST(sm_memeq(hdr,
				sm_cstr_data(ibdb_hdrmod->ibh_hdr),
				sm_cstr_getlen(ibdb_hdrmod->ibh_hdr)));
			SM_CSTR_FREE(ibdb_hdrmod->ibh_hdr);

			ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod
					, &status);
		}

		SM_TEST(ret == RT_IBDB_TA);
		if (sm_is_err(ret))
			break;

		/* check ta content?? */
		SM_CSTR_FREE(ibdb_ta->ibt_cdb_id);

		for (j = 0; j < nrcpts; j++)
		{
			sm_str_clr(str);
			ret = sm_strprintf(str,
					"rcpt-%d-%d@other.Rcpt", i, j);

			ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod
					, &status);
			SM_TEST(ret == RT_IBDB_RCPT);
			if (sm_is_err(ret))
				goto end;
			SM_TEST(sm_memeq(sm_str_getdata(str),
				sm_str_getdata(rcpt->ibr_pa),
				sm_str_getlen(rcpt->ibr_pa)));
		}

		ret = ibdbr_get(ibdbr_ctx, rcpt, ibdb_ta, ibdb_hdrmod, &status);

		if (sm_is_err(ret) &&
		    WRIBDB_IS_FLAG(flags, WRIBDB_FL_TRUNCATE) &&
		    WRIBDB_IS_FLAG(flags, WRIBDB_FL_TRUNCATED))
		{
			SM_TEST(SM_IO_EOF == ret ||
				sm_error_value(ret) == ENOENT ||
				sm_error_perm(SM_EM_IO, EIO) == ret);
			if (!(SM_IO_EOF == ret ||
			      sm_error_value(ret) == ENOENT ||
			      sm_error_perm(SM_EM_IO, EIO) == ret))
				sm_io_fprintf(smioerr,
					"sev=ERR, where=2, ibdbr_get%=#x\n"
					, ret);
		}
		else
		{
			SM_TEST(ret == RT_IBDB_TA);
			if (sm_is_err(ret))
				break;

			/* check ta content?? */
		}
		SM_CSTR_FREE(ibdb_ta->ibt_cdb_id);
	}

  end:
  error:
	if (ibdb_ta != NULL)
	{
		SM_STR_FREE(ibdb_ta->ibt_mail_pa);
		sm_free(ibdb_ta);
	}
	if (rcpt != NULL)
	{
		SM_STR_FREE(rcpt->ibr_pa);
		sm_free(rcpt);
	}
	SM_STR_FREE(str);
	if (ibdbr_ctx != NULL)
	{
		ret = ibdbr_close(ibdbr_ctx);
		SM_TEST(ret == SM_SUCCESS);
	}
	return ret;
}

static void
usage(const char *prg)
{
	sm_io_fprintf(smioerr,
		"usage: %s [options]\n"
		"-1	write one (open) transaction at begin\n"
		"-a	test header append records\n"
		"-I	generate incomplete IBDB file\n"
		"-i n	n iterations\n"
		"-l n	use length n for header modification string\n"
		"-m n	maximum size of IBDB file\n"
		"-p	test header prepend records\n"
		"-R	test header removal records\n"
		"-r	read only\n"
		"-t n	truncate last IBDB file by n bytes before reading\n"
		"-V	increase verbosity\n"
		"-w	write only\n"
		, prg);
	exit(EX_USAGE);
}

int
main(int argc, char *argv[])
{
	int iter, c, maxs;
	sm_ret_T ret;
	uint flags;
	bool readonly;
	bool writeonly;
	bool incomplete;
	char *base_dir;

	iter = 100;
	flags = 0;
	maxs = IBDBSIZE;
	readonly = false;
	writeonly = false;
	incomplete = false;
	base_dir = NULL;
	while ((c = getopt(argc, argv, "1ab:HIi:l:m:n:pRrt:Vw")) != -1)
	{
		switch (c)
		{
		  case '1':
			flags |= WRIBDB_FL_1OPENTA;
			break;
		  case 'a':
			flags |= WRIBDB_FL_HDR_APP;
			break;
		  case 'b':
			base_dir = strdup(optarg);
			if (NULL == base_dir)
				return EX_OSERR;
			break;
#if SM_HEAP_CHECK
		  case 'H':
			SmHeapCheck = atoi(optarg);
			break;
#endif
		  case 'I':
			incomplete = true;
			flags |= WRIBDB_FL_INCOMPL;
			break;
		  case 'i':
			iter = strtol(optarg, NULL, 0);
			if (iter <= 0)
				return EX_USAGE;
			break;
		  case 'l':
			long_str_len = strtol(optarg, NULL, 0);
			if (long_str_len <= 256)
				return EX_USAGE;
			flags |= WRIBDB_FL_LONG;
			break;
		  case 'm':
			maxs = strtol(optarg, NULL, 0);
			if (maxs <= 0)
				return EX_USAGE;
			break;
		  case 'n':
			rcpts_per_ta = strtol(optarg, NULL, 0);
			if (rcpts_per_ta <= 0)
				return EX_USAGE;
			break;
		  case 'p':
			flags |= WRIBDB_FL_HDR_PRE;
			break;
		  case 'R':
			flags |= WRIBDB_FL_HDR_REM;
			break;
		  case 'r':
			readonly = true;
			break;
		  case 't':
			n_trunc = strtoul(optarg, NULL, 0);
			if (n_trunc <= 0)
				return EX_USAGE;
			flags |= WRIBDB_FL_TRUNCATE;
			break;
		  case 'V':
			++Verbose;
			break;
		  case 'w':
			writeonly = true;
			break;
		  default:
			usage(argv[0]);
			/* NOTREACHED */
			return EX_USAGE;
		}
	}

	sm_test_begin(argc, argv, "test sm_idb_0");
	if (readonly)
		ret = SM_SUCCESS;
	else
		ret = testidbwr(base_dir, iter, maxs, flags, NULL, NULL);
	if (ret == SM_SUCCESS && !incomplete && !writeonly)
		ret = testidbr(base_dir, iter, maxs, flags);
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1