/*
 * Copyright (c) 2003-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-idbrecover-2.c,v 1.28 2007/06/18 04:42:30 ca Exp $")

#define QMGR_DEBUG_DEFINE 1
#include "sm/io.h"
#include "sm/mta.h"
#include "sm/ibdb.h"
#include "sm/cdb.h"
#include "sm/qmgr.h"
#include "sm/memops.h"
#include "sm/bhtable.h"
#include "sm/test.h"
#include <stdio.h>

/*
**  Test program to recover content of IBDB.
**  Performs a recovery run using ibdbrcvr_open() from "../qmgr/rdibdb.c"
*/

static int debug = 0;

#define SM_TEST_PRT	1

/* context for *_action() calls (and probably others later on) */
typedef struct te_ctx_S	te_ctx_T, *te_ctx_P;

struct te_ctx_S
{
	bht_P	 tex_crt_bht;
	bht_P	 tex_crt_bhr;
	bht_P	 tex_rd_bht;
	bht_P	 tex_rd_bhr;
	uint	 tex_ntas;
	uint	 tex_nrcpts;
};


/* HACK */
#include "../qmgr/rdibdb.c"

/* dummy function */
sm_ret_T
edb_reql_free(edb_ctx_P edb_ctx, edb_req_hd_P edb_req_hd)
{
	return SM_SUCCESS;
}

#define FNAME	"ibd"
#define IBDBSIZE	8192
#define INIT_SEQ	1
static int init_seq = INIT_SEQ;

static sm_ret_T
rdibdb(ri_ctx_P ri_ctx)
{
	uint32_t first, last;
	sm_ret_T ret, ibdb_which, res;
	bool once;
	ibdbr_ctx_P ibdbrc;

	/* Initialize most variables */
	ibdbrc = NULL;
	ibdb_which = 0;
	once = false;
	ret = SM_SUCCESS;

  again:
	/* Prepare for recovery */
	ibdb_which = ibdbrcvr_open();
	QM_LEV_DPRINTF(4, (QM_DEBFP, "qm_rdibdb running; once=%d, ibdbrcvr_open=%x\n", once, ibdb_which));
	if (sm_is_err(ibdb_which))
	{
		SM_TEST(ibdb_which == SM_SUCCESS);
		goto end;
	}
	if (ibdb_which == SM_IBDB_NO_RCVR)
		goto end;

	ret = ibdbf_get_seq(NULL, IBDB_NAME, IBDB_OFL_RCVR, &first, &last);
	QM_LEV_DPRINTF(4, (QM_DEBFP, "qm_rdibdb: ibdbf_get_seq=%#x, first=%u, last=%u\n", ret, first, last));

	/* distinguish error values? e.g., no directory? */
	if (sm_is_err(ret))
	{
		ret = SM_SUCCESS;
		goto end;	/* XXX no data? */
	}

	ret = ibdbr_open(NULL, IBDB_NAME, SM_IO_RDONLY, first, 0 /* unused */,
			IBDB_OFL_RCVR, &ibdbrc);
	if (ret != SM_SUCCESS)
		goto end;
	ri_ctx->rix_ibdbrc = ibdbrc;

	ri_ctx->rix_time = time(NULLT);

	/* Actually read IBDB */
	ret = qm_ribdb(ri_ctx);
	if (sm_is_err(ret))
		goto end;

	if (first <= last && ret == SM_SUCCESS)
	{
		res = ibdbr_unlink(ibdbrc, first, last);
		QM_LEV_DPRINTF(2, (QM_DEBFP, "qm_rdibdb: ibdbr_unlink=%x\n", ret));
	}

  end:
	if (ibdbrc != NULL)
	{
		ret = ibdbr_close(ibdbrc);
		/* XXX Complain on error? */
		ibdbrc = NULL;
	}
	res = ibdbrcvr_close();	/* check result? */
	SM_TEST(res == SM_SUCCESS);
	QM_LEV_DPRINTF(3, (QM_DEBFP, "qm_rdibdb: ibdbrcvr_close=%x\n", ret));

	if (ibdb_which == SM_IBDB_RCVR_EXISTS)
	{
		if (once)
		{
			/* oops ... been there, done that? */
			QM_LEV_DPRINTF(0, (QM_DEBFP, "ERROR: endless loop in qm_rdibdb\n"));
		}
		else
		{
			once = true;
			goto again;
		}
	}

#if SM_TEST_PRT
	if (debug > 1)
	{
		sm_io_fprintf(smioout, "#TAs:   %u\n", ri_ctx->rix_ntas);
		sm_io_fprintf(smioout, "#RCPTs: %u\n", ri_ctx->rix_nrcpts);
		sm_io_flush(smioout);
	}
#endif /* SM_TEST_PRT */
	return ret;
}

/*
**  TA_ACTION -- Add transaction to DEFEDB (callback function)
**
**	Parameters:
**		info -- bht entry
**		ctx -- context
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
test_ta_action(bht_entry_P info, void *ctx)
{
	sm_ret_T ret;
	ibdb_ta_P ibdb_ta, ibdb_ta2;
	te_ctx_P te_ctx;

	te_ctx = (te_ctx_P) ctx;
	ibdb_ta = (ibdb_ta_P) info->bhe_value;
	if (ibdb_ta->ibt_ta_id == NULL)
		return sm_err_perm(EINVAL);

	++te_ctx->tex_ntas;

	if (debug > 2)
		sm_io_fprintf(smioout, "OPEN TA: ta=%s\n", info->bhe_key);

	ibdb_ta2 = bht_find(te_ctx->tex_rd_bht, info->bhe_key, SMTP_STID_SIZE);
	SM_TEST(ibdb_ta2 != NULL);
	if (ibdb_ta2 == NULL)
	{
		/* OOPS what's going on? Where's our TA? */
		sm_io_fprintf(smioout, "ERROR: bht_find(%s) failed\n", info->bhe_key);
		return sm_err_perm(ENOENT);
	}

#if 0
	sm_io_fprintf(smioout, "OPEN TA: ta=%s, ", ibdb_ta->ibt_ta_id);
	sm_io_fprintf(smioout, "cdb=%C, ", ibdb_ta->ibt_cdb_id);
	sm_io_fprintf(smioout, "mail_pa=%S, ", ibdb_ta->ibt_mail_pa);
	sm_io_fprintf(smioout, "\n");
#endif /* SM_TEST_PRT */

	return SM_SUCCESS;
}

/*
**  RCPT_ACTION -- Add recipient to DEFEDB (callback function)
**
**	Parameters:
**		info -- bht entry
**		ctx -- context
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
test_rcpt_action(bht_entry_P info, void *ctx)
{
	sm_ret_T ret;
	ibdb_rcpt_P ibdb_rcpt, ibdb_rcpt2;
	te_ctx_P te_ctx;

	te_ctx = (te_ctx_P) ctx;
	ibdb_rcpt = (ibdb_rcpt_P) info->bhe_value;
	if (ibdb_rcpt->ibr_ta_id == NULL)
		return sm_err_perm(EINVAL);

	++te_ctx->tex_nrcpts;

	if (debug > 2)
		sm_io_fprintf(smioout, "OPEN RCPT: ta=%s-%d\n",
			ibdb_rcpt->ibr_ta_id,
			ibdb_rcpt->ibr_idx);

	ibdb_rcpt2 = bht_find(te_ctx->tex_rd_bhr, info->bhe_key,
				SMTP_RCPTID_SIZE);
	SM_TEST(ibdb_rcpt2 != NULL);
	if (ibdb_rcpt2 == NULL)
	{
		/* OOPS what's going on? Where's our rcpt? */
		sm_io_fprintf(smioout, "ERROR: bht_find(%s) failed\n", info->bhe_key);
		return sm_err_perm(ENOENT);
	}

#if 0
	sm_io_fprintf(smioout, "rcpt_pa=%S, ", ibdb_rcpt->ibr_pa);
	sm_io_fprintf(smioout, "rcpt_idx=%d, ", ibdb_rcpt->ibr_idx);
	sm_io_fprintf(smioout, "\n");
#endif /* SM_TEST_PRT */

	return SM_SUCCESS;
}

/*
**  TESTIDB -- read IBDB
**
**	Parameters:
**		None
**
**	Returns:
**		usual sm_error code.
*/

static sm_ret_T
testidbr(void)
{
	sm_ret_T ret;
	ri_ctx_P ri_ctx;
	bht_P bht, bhr;
	te_ctx_T te_ctx;

	bht = NULL;
	bhr = NULL;
	ret = sm_err_temp(ENOMEM);
	bht = bht_new(BHTSIZE, BHTSIZE);
	if (bht == NULL)
		goto error;
	bhr = bht_new(BHTSIZE, BHTSIZE);
	if (bhr == NULL)
		goto error;
	te_ctx.tex_crt_bht = bht;
	te_ctx.tex_crt_bhr = bhr;
	te_ctx.tex_ntas = 0;
	te_ctx.tex_nrcpts = 0;

	ret = qm_ri_ctx_new(NULL, &ri_ctx);
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
		return ret;
	te_ctx.tex_rd_bht = ri_ctx->rix_bht_ta;
	te_ctx.tex_rd_bhr = ri_ctx->rix_bht_rcpt;

	/* HACK */
	ret = rdibdb(ri_ctx);
	SM_TEST(ret == SM_SUCCESS);

	/* check recipients first to count them for TAs */
	bht_walk(bhr, test_rcpt_action, &te_ctx);

	/* Now run through open transactions */
	bht_walk(bht, test_ta_action, &te_ctx);

#if 0
	/* don't match... open != all */
	SM_TEST(ri_ctx->rix_ntas == te_ctx.tex_ntas );
	SM_TEST(ri_ctx->rix_nrcpts == te_ctx.tex_nrcpts);
#endif /* 0 */

#if SM_TEST_PRT
	if (debug > 1)
	{
		sm_io_fprintf(smioout, "C#TAs:   %u\n", te_ctx.tex_ntas);
		sm_io_fprintf(smioout, "C#RCPTs: %u\n", te_ctx.tex_nrcpts);
		sm_io_flush(smioout);
	}
#endif /* SM_TEST_PRT */

	/* HACK to avoid free... */
	ri_ctx->rix_bht_ta = NULL;
	ri_ctx->rix_bht_rcpt = NULL;
	ret = qm_ri_ctx_free(ri_ctx);
	SM_TEST(ret == SM_SUCCESS);
	return ret;

  error:
	return ret;
}

static void
usage(const char *prg)
{
	fprintf(stderr, "usage: %s [options]\n", prg);
	fprintf(stderr, "-d n	set debug level\n");
	fprintf(stderr, "-I n	initialize IBDB file number\n");
	exit(1);
}

int
main(int argc, char *argv[])
{
	int c;
	sm_ret_T ret;

	debug = 0;
	while ((c = getopt(argc, argv, "d:I:")) != -1)
	{
		switch (c)
		{
		  case 'd':
			debug = strtoul(optarg, NULL, 0);
			break;
		  case 'I':
			init_seq = atoi(optarg);
			break;
		  default:
			usage(argv[0]);
			return 1;
		}
	}

	sm_test_begin(argc, argv, "test sm_idbr_2");
	ret = testidbr();
	return sm_test_end();
}


syntax highlighted by Code2HTML, v. 0.9.1