/*
 * Copyright (c) 2004, 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: edbci.c,v 1.8 2006/03/13 18:41:33 ca Exp $")
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/heap.h"
#include "sm/assert.h"
#include "sm/str.h"
#include "sm/edbc.h"
#include "edbc.h"
#include "sm/qmgrdbg.h"

/*
**  EDBC_INSERT -- insert an entry to envelope database cache; may remove
**	"older" entries
**
**	Parameters:
**		edbc_ctx -- EDBC context
**		rcpt_id -- Recipient Id
**		next_try -- Time for next try
**		check -- check first whether entry exists
**
**	Returns:
**		usual sm_error code.
**
**	Locking: must be performed by caller.
**
**	Last code review:
**	Last code change:
*/

sm_ret_T
edbc_insert(edbc_ctx_P edbc_ctx, rcpt_id_T rcpt_id, time_T next_try, bool check)
{
	sm_ret_T ret;
	bool isfull;
	edbc_tnode_P edbc_tnode, edbc_tnode_ret, edbc_tnode_last;
	edbc_node_P edbc_node;

	SM_REQUIRE(edbc_ctx != NULL);
	if (check && edbc_exists(edbc_ctx, rcpt_id, NULL))
		return SM_SUCCESS;	/* other code? */

	isfull = (edbc_ctx->edbc_max_entries > 0 &&
		  edbc_ctx->edbc_entries >= edbc_ctx->edbc_max_entries);
	if (isfull)
	{
		edbc_tnode_last = RB_MAX(edbc_tree_S, &edbc_ctx->edbc_root);
		edbc_node = ECNL_FIRST(&edbc_tnode_last->ectn_hd);
		if (edbc_node->ecn_next_try <= next_try)
			return sm_error_temp(SM_EM_Q_EDBC, SM_E_FULL);
		edbc_node = NULL;
	}
	else
		edbc_tnode_last = NULL;

	edbc_tnode = NULL;
	edbc_node = (edbc_node_P) sm_zalloc(sizeof(*edbc_node));
	if (edbc_node == NULL)
		return sm_error_temp(SM_EM_Q_EDBC, ENOMEM);
	edbc_tnode = (edbc_tnode_P) sm_zalloc(sizeof(*edbc_tnode));
	if (edbc_tnode == NULL)
	{
		sm_free_size(edbc_node, sizeof(*edbc_node));
		return sm_error_temp(SM_EM_Q_EDBC, ENOMEM);
	}

	/*
	**  Remove last entry after data has been allocated:
	**  advantage: allocation succeeded, hence it will be added successfully
	**  disadvantage: if allocation fails it may have succeeded if the
	**	entry would have been removed first
	*/

	if (isfull)
	{
		ret = edbct_rm(edbc_ctx, edbc_tnode_last);
		if (sm_is_err(ret))
			goto error;
	}

	RCPT_ID_COPY(edbc_node->ecn_rcpt_id, rcpt_id);
	edbc_node->ecn_next_try = next_try;
	ECNL_INIT(&edbc_tnode->ectn_hd);
	ECNL_APP(&edbc_tnode->ectn_hd, edbc_node);
	edbc_tnode_ret = RB_FIND(edbc_tree_S, &edbc_ctx->edbc_root,
				edbc_tnode);
	if (edbc_tnode_ret == NULL)
	{
		edbc_tnode_ret = RB_INSERT(edbc_tree_S, &edbc_ctx->edbc_root,
					edbc_tnode);
		if (edbc_tnode_ret != NULL)
		{
			/* Internal error? Should not happen! */
			ret = sm_error_perm(SM_EM_Q_EDBC, EEXIST);
			goto error;
		}
	}
	else
	{
		/* Check first whether it's there? */
		ECNL_APP(&edbc_tnode_ret->ectn_hd, edbc_node);
		sm_free_size(edbc_tnode, sizeof(*edbc_tnode));
	}
	++edbc_ctx->edbc_entries;
	return SM_SUCCESS;

  error:
	SM_FREE(edbc_tnode);
	SM_FREE(edbc_node);
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1