/*
 * 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.
 *
 *	$Id: wribdb.c,v 1.41 2007/06/18 04:42:30 ca Exp $
 */

#include "sm/qmibdb.h"

#ifndef DONTWR
#define dontwr(iter, id_count, rcpt_idx, isrcpt)	false
#endif

#ifndef TACANCEL
#define tacancel(iter, id_count)	false
#endif

/* flags for IBDB open() */
#define WRIBDB_FL_NONE		0x0000U
#define WRIBDB_FL_INCOMPL	0x0001U
#define WRIBDB_FL_RANDOM	0x0002U
#define WRIBDB_FL_HDR_PRE	0x0010U
#define WRIBDB_FL_HDR_APP	0x0020U
#define WRIBDB_FL_HDR_REM	0x0040U
#define WRIBDB_FL_LONG		0x0100U
#define WRIBDB_FL_1OPENTA	0x0200U
#define WRIBDB_FL_TRUNCATE	0x0400U
#define WRIBDB_FL_TRUNCATED	0x0800U

#define WRIBDB_IS_FLAG(flags, fl)	(((flags) & (fl)) != 0)

/* context for *_action() calls (and probably others later on) */
typedef struct cl_ctx_S	cl_ctx_T, *cl_ctx_P;

struct cl_ctx_S
{
	ibdb_ctx_P	 clx_ibdbc;
	bht_P		 clx_bht;
	bht_P		 clx_bhr;
};

static int rand_mod = 10;
static int long_str_len = 600;
static char *long_str = NULL;
static int rcpts_per_ta = 0;

/*
**  TA_CLOSE -- Close transactions (callback function)
**
**	Parameters:
**		info -- bht entry
**		ctx -- context
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
ta_close(bht_entry_P info, void *ctx)
{
	sm_ret_T ret;
	ibdb_ta_P ibdb_ta;
	cl_ctx_P cl_ctx;
	ibdb_ctx_P ibdb_ctx;

	cl_ctx = (cl_ctx_P) ctx;
	ibdb_ctx = cl_ctx->clx_ibdbc;
	ibdb_ta = (ibdb_ta_P) info->bhe_value;
	if (ibdb_ta->ibt_ta_id == NULL)
		return sm_err_perm(EINVAL);

	if (rand() % rand_mod == 0)
	{
		rcpt_id_T rcpt_id;
		uint j;
		ibdb_rcpt_P ibdb_rcptf;

#if SM_TEST_PRT
		if (debug > 1)
		{
			sm_io_fprintf(smioout, "CLOSE TA(%p): ",
					ibdb_ta);
			sm_io_fprintf(smioout, "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, "nrcpts=%d, ",
				ibdb_ta->ibt_nrcpts);
			sm_io_fprintf(smioout, "\n");
			sm_io_flush(smioout);
		}
#endif /* SM_TEST_PRT */

		for (j = 0; j < ibdb_ta->ibt_nrcpts; j++)
		{
			sm_snprintf(rcpt_id, SMTP_RCPTID_SIZE,
				SMTP_RCPTID_FORMAT,
				ibdb_ta->ibt_ta_id,
				(rcpt_idx_T) j);
			ibdb_rcptf = bht_find(cl_ctx->clx_bhr, rcpt_id,
						SMTP_RCPTID_SIZE);
			if (ibdb_rcptf == NULL)
				continue;

#if SM_TEST_PRT
			if (debug > 1)
			{
				sm_io_fprintf(smioout, "CLOSE ALSO RCPT:(%p) ta=%s, ",
					ibdb_rcptf, ibdb_ta->ibt_ta_id);
				sm_io_fprintf(smioout, "rcpt_idx=%d, ", j);
				sm_io_fprintf(smioout, "\n");
				sm_io_flush(smioout);
			}
#endif /* SM_TEST_PRT */

			/* XXX HACK */
			ret = ibdb_rcpt_status(ibdb_ctx, ibdb_rcptf,
					IBDB_RCPT_DONE, IBDB_FL_NOROLL,
					THR_LOCK_UNLOCK);
			SM_TEST(ret == SM_SUCCESS);
			bht_rm(cl_ctx->clx_bhr, rcpt_id, SMTP_RCPTID_SIZE,
				ibdb_rcpt_free, NULL);
		}

#if SM_TEST_PRT
		if (debug > 1)
		{
			sm_io_fprintf(smioout, "2CLOSE TA(%p): ",
					ibdb_ta);
			sm_io_fprintf(smioout, "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, "nrcpts=%d, ",
				ibdb_ta->ibt_nrcpts);
			sm_io_fprintf(smioout, "\n");
			sm_io_flush(smioout);
		}
#endif /* SM_TEST_PRT */
		ret = ibdb_ta_status(ibdb_ctx, ibdb_ta, IBDB_TA_DONE,
				IBDB_FL_NONE, 0, THR_LOCK_UNLOCK);
		SM_TEST(ret == SM_SUCCESS);
		bht_rm(cl_ctx->clx_bht, ibdb_ta->ibt_ta_id, SMTP_STID_SIZE,
			ibdb_ta_free, NULL);
	}
	return SM_SUCCESS;
}

/*
**  RCPT_CLOSE -- Close recipient (callback function)
**
**	Parameters:
**		info -- bht entry
**		ctx -- context
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
rcpt_close(bht_entry_P info, void *ctx)
{
	sm_ret_T ret;
	ibdb_rcpt_P ibdb_rcpt;
	cl_ctx_P cl_ctx;
	ibdb_ctx_P ibdb_ctx;

	cl_ctx = (cl_ctx_P) ctx;
	ibdb_ctx = cl_ctx->clx_ibdbc;
	ibdb_rcpt = (ibdb_rcpt_P) info->bhe_value;
	if (ibdb_rcpt->ibr_ta_id == NULL)
		return sm_err_perm(EINVAL);

	if (rand() % rand_mod == 0)
	{
		rcpt_id_T rcpt_id;

#if SM_TEST_PRT
		if (debug > 1)
		{
			sm_io_fprintf(smioout, "CLOSE RCPT: ta=%s, ",
					ibdb_rcpt->ibr_ta_id);
			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 */
		ret = ibdb_rcpt_status(ibdb_ctx, ibdb_rcpt, IBDB_RCPT_DONE,
				IBDB_FL_NOROLL, THR_LOCK_UNLOCK);
		SM_TEST(ret == SM_SUCCESS);
		sm_snprintf(rcpt_id, SMTP_RCPTID_SIZE,
			SMTP_RCPTID_FORMAT,
			ibdb_rcpt->ibr_ta_id,
			ibdb_rcpt->ibr_idx);
		bht_rm(cl_ctx->clx_bhr, rcpt_id, SMTP_RCPTID_SIZE,
			ibdb_rcpt_free, NULL);
	}
	return SM_SUCCESS;
}

/*
**  TESTIDB -- write INCEDB records
**
**	Parameters:
**		base_dir -- name of base directory (can by NULL)
**		iter -- number of iterations (transactions)
**		maxs -- maximum size of an INCEDB file
**		flags -- flags, see above
**		bht -- hash table for transactions (might be NULL)
**		bhr -- hash table for recipients (might be NULL)
**
**	Returns:
**		usual sm_error code.
*/

static int
testidbwr(const char *base_dir, int iter, int maxs, uint flags, bht_P bht, bht_P bhr)
{
	int i, j, nrcpts;
	sm_ret_T ret;
	ibdb_ctx_P ibdb_ctx;
	ibdb_ta_P ibdb_ta;
	ibdb_rcpt_P ibdb_rcpt;
	sessta_id_T ta_id;
	char cdb[32];
	id_count_T id_count;
	bht_entry_P bhte;
	rcpt_id_P rcpt_id;
	cl_ctx_T cl_ctx;
	ibdb_hdrmod_P ibdb_hdrmod;
	char hdr[SM_MAXHDRLEN];

/* XXX either use proper free/new functions or don't free entries... */

	long_str = NULL;
	id_count = 1; /* better for testing (reproducible) than time(NULLT); */
	ret = ibdb_open(base_dir, FNAME, SM_IO_WRONLY, init_seq, maxs,
			IBDB_OFL_WRITE, NULLPTR, &ibdb_ctx);
	SM_TEST(ret == SM_SUCCESS);
	if (ret != SM_SUCCESS)
	{
		sm_io_fprintf(smioerr, "ibdb_open=%#x\n", ret);
		return ret;
	}

	ibdb_rcpt = NULL;
	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_rcpt = (ibdb_rcpt_P) sm_zalloc(sizeof(*ibdb_rcpt));
	SM_TEST(ibdb_rcpt != NULL);
	if (ibdb_rcpt == NULL)
		goto end;
	ibdb_rcpt->ibr_ta_id = ta_id;
	ibdb_rcpt->ibr_pa = sm_str_new(NULL, 256, 256);
	SM_TEST(ibdb_rcpt->ibr_pa != NULL);
	if (ibdb_rcpt->ibr_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;

	if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_1OPENTA))
	{
		i = INT_MAX;
		sm_snprintf(ta_id, SMTP_STID_SIZE, SMTPS_STID_FORMAT, id_count,
			2);
		sm_str_clr(ibdb_ta->ibt_mail_pa);
		ret = sm_strprintf(ibdb_ta->ibt_mail_pa,
				"sender-%d@some.domain", i);
		ret = sm_snprintf(cdb, sizeof(cdb), "cdb%010d", i);
		ibdb_ta->ibt_cdb_id = sm_cstr_scpyn((const uchar *) cdb,
						strlen(cdb));
		SM_TEST_E(ibdb_ta->ibt_cdb_id != NULL);
		j = INT_MAX > 2;
		sm_str_clr(ibdb_rcpt->ibr_pa);
		ret = sm_strprintf(ibdb_rcpt->ibr_pa,
					"rcpt-%d-%d@other.Rcpt", i, j);
		ibdb_rcpt->ibr_idx = (rcpt_idx_T) j;
		ret = ibdb_rcpt_status(ibdb_ctx, ibdb_rcpt,
			IBDB_RCPT_NEW, IBDB_FL_NONE, THR_LOCK_UNLOCK);
		SM_TEST_E(ret == SM_SUCCESS);

		ret = ibdb_ta_status(ibdb_ctx, ibdb_ta, IBDB_TA_NEW,
				IBDB_FL_NONE, 0, THR_LOCK_UNLOCK);
		SM_TEST_E(ret == SM_SUCCESS);
	}

	for (i = 0; i < iter; i++)
	{
		id_count++;
		sm_snprintf(ta_id, SMTP_STID_SIZE, SMTPS_STID_FORMAT, id_count,
			2);
		sm_str_clr(ibdb_ta->ibt_mail_pa);
		ret = sm_strprintf(ibdb_ta->ibt_mail_pa,
				"sender-%d@some.domain", i);
		ret = sm_snprintf(cdb, sizeof(cdb), "cdb%010d", i);
		ibdb_ta->ibt_cdb_id = sm_cstr_scpyn((const uchar *) cdb,
						strlen(cdb));
		SM_TEST(ibdb_ta->ibt_cdb_id != NULL);
		if (ibdb_ta->ibt_cdb_id == NULL)
			goto end;
		nrcpts = ibdb_ta->ibt_nrcpts = (rcpts_per_ta > 0)
						? rcpts_per_ta : (i % 5) + 1;

		/* first: write some recipients */
		for (j = 0; j < nrcpts; j++)
		{
			sm_str_clr(ibdb_rcpt->ibr_pa);
			ret = sm_strprintf(ibdb_rcpt->ibr_pa,
					"rcpt-%d-%d@other.Rcpt", i, j);
			ibdb_rcpt->ibr_idx = (rcpt_idx_T) j;

			ret = ibdb_rcpt_status(ibdb_ctx, ibdb_rcpt,
				IBDB_RCPT_NEW, IBDB_FL_NONE, THR_LOCK_UNLOCK);
			SM_TEST_E(ret == SM_SUCCESS);
		}

		if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_LONG))
		{
			ibdb_hdrmod->ibh_type = SM_HM_TYPE_PREPEND;
			if (NULL == long_str)
			{
				long_str = sm_malloc(long_str_len);
				SM_TEST_E(long_str != NULL);
			}
			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';
			ibdb_hdrmod->ibh_hdr = sm_cstr_crt((uchar *)long_str,
							strlen(long_str));
			ret = ibdb_hdrmod_wr(ibdb_ctx, ibdb_hdrmod,
				IBDB_FL_NONE, THR_LOCK_UNLOCK);
			sm_free(ibdb_hdrmod->ibh_hdr);
			SM_TEST_E(ret == SM_SUCCESS);
		}

		if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_HDR_PRE))
		{
			ibdb_hdrmod->ibh_type = SM_HM_TYPE_PREPEND;
			ret = snprintf(hdr, sizeof(hdr),
					"HeaderPre: prepend ta=%s i=%d\r\n",
					ta_id, i);
			ibdb_hdrmod->ibh_hdr = sm_cstr_crt((uchar *)hdr,
							strlen(hdr));
			ret = ibdb_hdrmod_wr(ibdb_ctx, ibdb_hdrmod,
				IBDB_FL_NONE, THR_LOCK_UNLOCK);
			sm_free(ibdb_hdrmod->ibh_hdr);
			SM_TEST(ret == SM_SUCCESS);
			if (ret != SM_SUCCESS)
				goto end;
		}
		if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_HDR_REM))
		{
			ibdb_hdrmod->ibh_type = SM_HM_TYPE_REMOVE;
			ibdb_hdrmod->ibh_pos = 2;
			ibdb_hdrmod->ibh_hdr = NULL;
			ret = ibdb_hdrmod_wr(ibdb_ctx, ibdb_hdrmod,
				IBDB_FL_NONE, THR_LOCK_UNLOCK);
			SM_TEST(ret == SM_SUCCESS);
			if (ret != SM_SUCCESS)
				goto end;
		}
		if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_HDR_APP))
		{
			ibdb_hdrmod->ibh_type = SM_HM_TYPE_APPEND;
			ret = snprintf(hdr, sizeof(hdr),
					"HeaderApp: append ta=%s i=%d\r\n",
					ta_id, i);
			ibdb_hdrmod->ibh_hdr = sm_cstr_crt((uchar *)hdr,
							strlen(hdr));
			ret = ibdb_hdrmod_wr(ibdb_ctx, ibdb_hdrmod,
				IBDB_FL_NONE, THR_LOCK_UNLOCK);
			sm_free(ibdb_hdrmod->ibh_hdr);
			SM_TEST(ret == SM_SUCCESS);
			if (ret != SM_SUCCESS)
				goto end;
		}

		/* last: write mail for this transaction */
		ret = ibdb_ta_status(ibdb_ctx, ibdb_ta, IBDB_TA_NEW,
				IBDB_FL_NONE, 0, THR_LOCK_UNLOCK);
		SM_TEST(ret == SM_SUCCESS);
		if (ret != SM_SUCCESS)
			break;

		if (i != iter - 1 || !WRIBDB_IS_FLAG(flags, WRIBDB_FL_INCOMPL))
		{
			for (j = 0; j < nrcpts; j++)
			{
				ibdb_rcpt->ibr_idx = (rcpt_idx_T) j;
				sm_str_clr(ibdb_rcpt->ibr_pa);
				ret = sm_strprintf(ibdb_rcpt->ibr_pa,
						"rcpt-%d-%d@other.Rcpt", i, j);

				if (dontwr(iter, id_count, j, true) &&
				    !tacancel(iter, id_count) &&
				    bhr != NULL)
				{
					ibdb_rcpt_P ibdb_rcpt2;

					rcpt_id = (char *) sm_malloc(SMTP_RCPTID_SIZE + 1);
					SM_TEST(rcpt_id != NULL);
					if (rcpt_id == NULL)
					{
						ret = sm_err_temp(ENOMEM);
						goto end;
					}
					sm_snprintf(rcpt_id, SMTP_RCPTID_SIZE,
						SMTP_RCPTID_FORMAT,
						ibdb_rcpt->ibr_ta_id,
						ibdb_rcpt->ibr_idx);
					ret = qm_ibdb_rcpt_new(&ibdb_rcpt2);
					SM_TEST(ret == SM_SUCCESS);
					if (ret != SM_SUCCESS)
						goto end;
					SESSTA_COPY(ibdb_rcpt2->ibr_ta_id, 
						ibdb_rcpt->ibr_ta_id);
					ret = sm_str_cpy(ibdb_rcpt2->ibr_pa,
						ibdb_rcpt->ibr_pa);
					SM_TEST(ret == SM_SUCCESS);
					if (ret != SM_SUCCESS)
						goto end;

					ibdb_rcpt2->ibr_idx = ibdb_rcpt->ibr_idx;
					ret = bht_add(bhr,
						rcpt_id, SMTP_RCPTID_SIZE,
						ibdb_rcpt2, &bhte);
					SM_TEST(ret == SM_SUCCESS);

#if SM_TEST_PRT
					if (debug > 3)
					{
						sm_io_fprintf(smioout,
							"ADD RCPT(%p): ta=%s, ",
							ibdb_rcpt2,
							ibdb_rcpt2->ibr_ta_id);
						sm_io_fprintf(smioout,
							"rcpt_idx=%04d\n", j);
					}
#endif /* SM_TEST_PRT */
					continue;
				}

				ret = ibdb_rcpt_status(ibdb_ctx, ibdb_rcpt,
						IBDB_RCPT_DONE, IBDB_FL_NOROLL,
						THR_LOCK_UNLOCK);
				SM_TEST(ret == SM_SUCCESS);
				if (ret != SM_SUCCESS)
					goto end;
			}
			if (!dontwr(iter, id_count, 0, false))
			{
				ret = ibdb_ta_status(ibdb_ctx, ibdb_ta,
					IBDB_TA_DONE, IBDB_FL_NONE, 0,
					THR_LOCK_UNLOCK);
				SM_TEST(ret == SM_SUCCESS);
				if (ret != SM_SUCCESS)
					break;
			}
			else if (tacancel(iter, id_count))
			{
				ret = ibdb_ta_status(ibdb_ctx, ibdb_ta,
					IBDB_TA_CANCEL, IBDB_FL_NONE, 0,
					THR_LOCK_UNLOCK);
				SM_TEST(ret == SM_SUCCESS);
				if (ret != SM_SUCCESS)
					break;
			}
			else if (bht != NULL)
			{
				ibdb_ta_P ibdb_ta2;

				ret = qm_ibdb_ta_new(&ibdb_ta2);
				SM_TEST(ret == SM_SUCCESS);
				if (ret != SM_SUCCESS)
					goto end;
				SESSTA_COPY(ibdb_ta2->ibt_ta_id,
						ibdb_ta->ibt_ta_id);
				ibdb_ta2->ibt_cdb_id = SM_CSTR_DUP(ibdb_ta->ibt_cdb_id);
				ibdb_ta2->ibt_nrcpts = ibdb_ta->ibt_nrcpts;
				ret = sm_str_cpy(ibdb_ta2->ibt_mail_pa,
					ibdb_ta->ibt_mail_pa);
				SM_TEST(ret == SM_SUCCESS);
				ret = bht_add(bht,
					ibdb_ta2->ibt_ta_id, SMTP_STID_SIZE,
					ibdb_ta2, &bhte);
				SM_TEST(ret == SM_SUCCESS);

#if SM_TEST_PRT
				if (debug > 3)
				{
					sm_io_fprintf(smioout,
						"ADD TA(%p): ta=%s\n",
						ibdb_ta2,  ibdb_ta2->ibt_ta_id);
				}
#endif /* SM_TEST_PRT */
			}
		}
		sm_cstr_free(ibdb_ta->ibt_cdb_id);

		/* number of records per file */
		j = maxs / IBDB_REC_SIZE;
		if (j <= 0)
			j = 1;

		/* number of logfiles written */
		j = iter / j;
		if (j > 0 && (j % 10) == 0)
		{
			ret = ibdb_clean(ibdb_ctx, THR_LOCK_UNLOCK);
			SM_TEST(ret == SM_SUCCESS);
		}
	}
	if (WRIBDB_IS_FLAG(flags, WRIBDB_FL_RANDOM) &&
	    bht != NULL && bhr != NULL)
	{
		cl_ctx.clx_ibdbc = ibdb_ctx;
		cl_ctx.clx_bht = bht;
		cl_ctx.clx_bhr = bhr;
		bht_walk(bhr, rcpt_close, &cl_ctx);
		bht_walk(bht, ta_close, &cl_ctx);
	}

  end:
  error:
	if (ibdb_ta != NULL)
	{
		SM_STR_FREE(ibdb_ta->ibt_mail_pa);
		sm_free(ibdb_ta);
	}
	if (ibdb_rcpt != NULL)
	{
		SM_STR_FREE(ibdb_rcpt->ibr_pa);
		sm_free(ibdb_rcpt);
	}
	ret = ibdb_close(ibdb_ctx);
	SM_TEST(ret == SM_SUCCESS);
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1