/*
 * 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_RCSID("@(#)$Id: edbr.c,v 1.19 2006/05/02 17:13:38 ca Exp $")
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/assert.h"
#include "sm/reccom.h"
#include "sm/rcb.h"
#include "sm/qmgr-int.h"
#include "sm/actdb-int.h"
#include "edb-int.h"
#include "sm/edb.h"
#include "sm/pthread.h"

/*
**  EDB_RD_OPEN -- open a read cursor
**
**	Parameters:
**		edb_ctx -- EDB context
**		pedb_cursor -- pointer to EDB cursor (output)
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
edb_rd_open(edb_ctx_P edb_ctx, edb_cursor_P *pedb_cursor)
{
	int r;

	SM_ASSERT(edb_ctx != NULL);
	SM_ASSERT(pedb_cursor != NULL);

	r = edb_ctx->edb_bdb->cursor(edb_ctx->edb_bdb, NULL, pedb_cursor, 0);
	if (r != 0)
		return sm_error_perm(SM_EM_EDB, r);
	return SM_SUCCESS;
}

/*
**  EDB_RD_CLOSE -- close read cursor
**
**	Parameters:
**		edb_ctx -- EDB context
**		edb_cursor -- EDB cursor
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
edb_rd_close(edb_ctx_P edb_ctx, edb_cursor_P edb_cursor)
{
	int r;

	SM_ASSERT(edb_ctx != NULL);
	SM_ASSERT(edb_cursor != NULL);

	r = edb_cursor->c_close(edb_cursor);
	if (r != 0)
		return sm_error_perm(SM_EM_EDB, r);
	return SM_SUCCESS;
}


/*
**  EDB_RD_NEXT -- read next entry
**
**	Parameters:
**		edb_ctx -- EDB context
**		edb_cursor -- EDB cursor
**		edb_req -- request: contains key to read, rcb to fill (I/O)
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
edb_rd_next(edb_ctx_P edb_ctx, edb_cursor_P edb_cursor, edb_req_P edb_req)
{
	sm_rcb_P rcb;
	sm_ret_T ret;
	int r;
	size_t l;
	DBT db_key, db_data;

	SM_ASSERT(edb_ctx != NULL);
	SM_ASSERT(edb_req != NULL);
	r = pthread_mutex_lock(&edb_ctx->edb_mutex);
	SM_LOCK_OK(r);
	if (r != 0)
	{
		/* LOG? */
		return sm_error_perm(SM_EM_EDB, r);
	}

	/* XXX This should be a transaction... check BDB doc */
	ret = SM_SUCCESS;
	rcb = edb_req->edb_req_rcb;
	sm_memzero(&db_key, sizeof(db_key));
	sm_memzero(&db_data, sizeof(db_data));

	db_key.flags = DB_DBT_USERMEM;
	db_key.data = edb_req->edb_req_id;
	db_key.ulen = sizeof(edb_req->edb_req_id); /* SMTP_ID_SIZE */

	db_data.flags = DB_DBT_USERMEM;
	db_data.data = sm_rcb_data(rcb);
	db_data.ulen = sm_rcb_getsize(rcb);

	ret = SM_SUCCESS;
	r = edb_cursor->c_get(edb_cursor, &db_key, &db_data, DB_NEXT);
	l = db_data.size;
	if (r == DB_BUFFER_SMALL && l < EDB_RC_MAXSZ && sm_rcb_getsize(rcb) < l)
	{
		l = (l + 1023) & ~1023;	/* round to 1024; see BDB docs */
		if (!sm_is_err(sm_rcb_resize_data(rcb, l)))
		{
			db_data.data = sm_rcb_data(rcb);
			db_data.ulen = sm_rcb_getsize(rcb);
			r = edb_cursor->c_get(edb_cursor, &db_key, &db_data,
					DB_NEXT);
			l = db_data.size;
		}
	}
	if (r != 0)
		ret = sm_error_perm(SM_EM_EDB, r);
	/* How to handle DB_NOTFOUND? */
	else if (l < sm_rcb_getmax(rcb))
		sm_rcb_setlen(edb_req->edb_req_rcb, db_data.size);

	/* fall through for unlocking */
	r = pthread_mutex_unlock(&edb_ctx->edb_mutex);
	SM_ASSERT(r == 0);
	if (r != 0 && sm_is_success(ret))
		ret = sm_error_perm(SM_EM_EDB, r);

	/* cleanup? */
	return ret;
}

/*
**  EDB_GET_TYPE -- extract type from edb_req
**
**	Parameters:
**		edb_req -- request: contains key to read, rcb to fill (I/O)
**
**	Returns:
**		>0: type
**		<0: usual sm_error code
*/

sm_ret_T
edb_get_type(edb_req_P edb_req)
{
	sm_rcb_P rcb;
	sm_ret_T ret;
	uint32_t v, l, rt, tl;

	SM_ASSERT(edb_req != NULL);
	rcb = edb_req->edb_req_rcb;
	ret = sm_rcb_open_dec(rcb);

	/* Total length of record */
	ret = sm_rcb_getuint32(rcb, &tl);
	if (sm_is_err(ret))
		goto error;
	if (tl > EDB_RC_MAXSZ || tl > sm_rcb_getlen(rcb))
		goto err0;

	/* transaction status */
	ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
	if (sm_is_err(ret))
		goto error;
	if (l != 4 || (rt != RT_EDB_TA_ST && rt != RT_EDBR_ST
			&& rt != RT_EDB_VERSION))
		goto err0;

	(void) sm_rcb_close_dec(rcb);
	if (sm_is_err(ret))
		goto error;
	return (rt == RT_EDB_TA_ST) ? EDB_REQ_TA
		: ((rt == RT_EDBR_ST) ? EDB_REQ_RCPT : EDB_REQ_VRS);

  err0:
	ret = sm_error_perm(SM_EM_EDB, SM_E_PR_ERR);
  error:
	(void) sm_rcb_close_decn(rcb);

	/* cleanup? */
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1