/*
 * 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: edbcmv.c,v 1.7 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_MV -- change an entry in envelope database cache
**
**	Parameters:
**		edbc_ctx -- edbc context
**		edbc_node -- edbc node
**		next_try -- next time to try
**
**	Returns:
**		usual sm_error code.
**
**	Locking: must be performed by caller.
*/

sm_ret_T
edbc_mv(edbc_ctx_P edbc_ctx, edbc_node_P edbc_node, time_T next_try)
{
	sm_ret_T ret;
	edbc_node_T edbc_node_search;
	edbc_tnode_T edbc_tnode;
	edbc_tnode_P edbc_tnode_ret, edbc_tnode_new;

	/*
	**  Note: this function isn't as useful as it was supposed to be
	**  because it was intended to be "always" successful even if
	**  resources are unavailable.  However, that doesn't work because
	**  it may be required to create a new tnode.  Currently it is always
	**  allocated to avoid problems "in the middle" of the operation.
	**
	**  The algorithm should be like this:
	**  locate tnode of current place (tnode_old)
	**  locate tnode of new place (tnode_new)
	**  if tnode_new == NULL
	**	if tnode_old contains only one node: reuse this tnode
	**	else { alloc new tnode; if NULL: return error; }
	**  now all resources are available, the old data can be
	**  removed, the new brought into place...
	*/

	SM_REQUIRE(edbc_ctx != NULL);
	SM_REQUIRE(edbc_node != NULL);

	edbc_tnode_new = NULL;
	sm_memzero(&edbc_node_search, sizeof(edbc_node_search));
	sm_memzero(&edbc_tnode, sizeof(edbc_tnode));
	ECNL_INIT(&(edbc_tnode.ectn_hd));
	ECNL_APP(&(edbc_tnode.ectn_hd), &edbc_node_search);
	edbc_node_search.ecn_next_try = edbc_node->ecn_next_try;
	edbc_tnode_ret = RB_FIND(edbc_tree_S, &edbc_ctx->edbc_root,
				&edbc_tnode);
	if (edbc_tnode_ret == NULL)
		return sm_error_temp(SM_EM_Q_EDBC, SM_E_NOTFOUND);

	/* this might not be needed... */
	edbc_tnode_new = (edbc_tnode_P) sm_zalloc(sizeof(*edbc_tnode_new));
	if (edbc_tnode_new == NULL)
		return sm_error_temp(SM_EM_Q_EDBC, ENOMEM);

	ECNL_REMOVE(&edbc_tnode_ret->ectn_hd, edbc_node);
	if (ECNL_EMPTY(&edbc_tnode_ret->ectn_hd))
	{
		if (RB_REMOVE(edbc_tree_S, &edbc_ctx->edbc_root,
				edbc_tnode_ret) == NULL)
		{
			/* OOPS now the entry is gone... */
			return sm_error_temp(SM_EM_Q_EDBC, SM_E_UNEXPECTED);
		}

		SM_FREE(edbc_tnode_ret);
		edbc_tnode_ret = NULL;
	}

	sm_memzero(&edbc_tnode, sizeof(edbc_tnode));
	ECNL_INIT(&(edbc_tnode.ectn_hd));
	ECNL_APP(&(edbc_tnode.ectn_hd), edbc_node);
	edbc_node->ecn_next_try = next_try;
	edbc_tnode_ret = RB_FIND(edbc_tree_S, &edbc_ctx->edbc_root,
				&edbc_tnode);
	if (edbc_tnode_ret == NULL)
	{
		ECNL_INIT(&edbc_tnode_new->ectn_hd);
		ECNL_APP(&edbc_tnode_new->ectn_hd, edbc_node);
		edbc_tnode_ret = RB_INSERT(edbc_tree_S, &edbc_ctx->edbc_root,
					edbc_tnode_new);
		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);
	}

	return SM_SUCCESS;

  error:
	SM_FREE(edbc_tnode_new);
	return ret;
}


syntax highlighted by Code2HTML, v. 0.9.1