/*
 * 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: rcbopen.c,v 1.15 2005/06/02 19:00:36 ca Exp $")

#include "sm/assert.h"
#include "sm/magic.h"
#include "sm/memops.h"
#include "sm/rpool.h"
#include "sm/str-int.h"
#include "sm/rcb.h"
#include "sm/str2rcb.h"
#include "sm/reccom.h"

/*
**  SM_RCB_OPEN_RCV -- Open rcb for receiving data from file.
**
**	Parameters:
**		rcb -- sm_rcb_P object.
**
**	Returns:
**		SM_SUCCESS
**
**	Last code review: 2005-03-29 17:32:35
**	Last code change:
*/

sm_ret_T
sm_rcb_open_rcv(sm_rcb_P rcb)
{
	SM_IS_RCB(rcb);
	SM_REQUIRE(rcb->sm_rcb_base != NULL);
#if SM_RCB_CHECK
	SM_REQUIRE(rcb->sm_rcb_state == SM_RCB_NONE);
#endif
	rcb->sm_rcb_len = 0;
	rcb->sm_rcb_rw = -1;
#if SM_RCB_CHECK
	rcb->sm_rcb_state = SM_RCB_RCV;
#endif
	return SM_SUCCESS;
}

/*
**  SM_RCB_OPEN_DEC -- Open rcb for decoding data.
**
**	Parameters:
**		rcb -- sm_rcb_P object.
**
**	Returns:
**		SM_SUCCESS (unless SM_RCB_END_RCB is set)
**
**	Last code review: 2005-03-29 05:21:31
**	Last code change:
*/

sm_ret_T
sm_rcb_open_dec(sm_rcb_P rcb)
{
#if SM_RCB_END_RCB
	uint32_t v, tl;
#endif

	SM_IS_RCB(rcb);
	SM_REQUIRE(rcb->sm_rcb_base != NULL);
#if SM_RCB_CHECK
	SM_REQUIRE(rcb->sm_rcb_state == SM_RCB_NONE);
#endif

#if SM_RCB_END_RCB
	if (rcb->sm_rcb_len < 12)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	if ((rcb->sm_rcb_len % 4) != 0)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	rcb->sm_rcb_rw = rcb->sm_rcb_len - 12;
	sm_buf2uint32(rcb->sm_rcb_base + rcb->sm_rcb_rw, &v);
	if (v != 4)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	rcb->sm_rcb_rw += sizeof(uint32_t);
	sm_buf2uint32(rcb->sm_rcb_base + rcb->sm_rcb_rw, &v);
	if (v != RT_END_OF_RCB)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	rcb->sm_rcb_rw += sizeof(uint32_t);
	sm_buf2uint32(rcb->sm_rcb_base + rcb->sm_rcb_rw, &v);
	sm_buf2uint32(rcb->sm_rcb_base, &tl);
	if (v != tl)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
#endif /* SM_RCB_END_RCB */

	rcb->sm_rcb_rw = 0;
#if SM_RCB_CHECK
	rcb->sm_rcb_state = SM_RCB_DEC;
#endif
	return SM_SUCCESS;
}

/*
**  SM_RCB_OPEN_ENC -- Open rcb for encoding data into it.
**
**	Parameters:
**		rcb -- sm_rcb_P object.
**		n -- 1. maximum size for encoded data, -1: no explicit limit
**			(just implicit by max length sm_rcb_max)
**		     2. minimum size requested for RCB.
**		fixme: should these be two different parameters??
**		Note: all applications use -1 (only test programs use
**			different values).
**
**	Returns:
**		usual sm_error code; ENOMEM, SM_E_OVFLW_NS
**
**	Side Effects: none on error.
**
**	Last code review: 2005-03-22 17:44:11
**	Last code change:
*/

sm_ret_T
sm_rcb_open_enc(sm_rcb_P rcb, int n)
{
	SM_IS_RCB(rcb);
	SM_REQUIRE(rcb->sm_rcb_base != NULL);
#if SM_RCB_CHECK
	SM_REQUIRE(rcb->sm_rcb_state == SM_RCB_NONE);
#endif
	rcb->sm_rcb_len = 0;
	if (n > 0 && (uint)n > rcb->sm_rcb_size)
	{
		uint u;

		u = (uint)n;
		SM_STR_INCREASE_R(rcb, u);
	}
	rcb->sm_rcb_rw = n;
#if SM_RCB_CHECK
	rcb->sm_rcb_state = SM_RCB_ENC;
#endif
	return SM_SUCCESS;
}

/*
**  SM_RCB_OPEN_SND -- Open rcb for sending data from it.
**
**	Parameters:
**		rcb -- sm_rcb_P object.
**
**	Returns:
**		SM_SUCCESS
**
**	Last code review: 2005-03-22 23:23:53
**	Last code change:
*/

sm_ret_T
sm_rcb_open_snd(sm_rcb_P rcb)
{
	SM_IS_RCB(rcb);
	SM_REQUIRE(rcb->sm_rcb_base != NULL);
#if SM_RCB_CHECK
	SM_REQUIRE(rcb->sm_rcb_state == SM_RCB_NONE);
#endif
	rcb->sm_rcb_rw = 0;
#if SM_RCB_CHECK
	rcb->sm_rcb_state = SM_RCB_SND;
#endif
	return SM_SUCCESS;
}

/*
**  SM_RCB_CLOSE_RCV -- Close rcb after receiving data from file.
**
**	Parameters:
**		rcb -- sm_rcb_P object.
**
**	Returns:
**		SM_SUCCESS (unless SM_RCB_END_RCB is set)
**
**	Last code review: 2005-03-29 05:20:13
**	Last code change:
*/

sm_ret_T
sm_rcb_close_rcv(sm_rcb_P rcb)
{
#if SM_RCB_END_RCB
	uint32_t v, tl;
#endif

	SM_IS_RCB(rcb);
	SM_REQUIRE(rcb->sm_rcb_base != NULL);
#if SM_RCB_CHECK
	SM_REQUIRE(rcb->sm_rcb_state == SM_RCB_RCV);
	SM_REQUIRE(rcb->sm_rcb_size >= rcb->sm_rcb_len);
	SM_REQUIRE(rcb->sm_rcb_size <= rcb->sm_rcb_max);
#endif

#if SM_RCB_END_RCB
	if (rcb->sm_rcb_len < 12)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	if ((rcb->sm_rcb_len % 4) != 0)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	rcb->sm_rcb_rw = rcb->sm_rcb_len - 12;
	sm_buf2uint32(rcb->sm_rcb_base + rcb->sm_rcb_rw, &v);
	if (v != 4)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	rcb->sm_rcb_rw += sizeof(uint32_t);
	sm_buf2uint32(rcb->sm_rcb_base + rcb->sm_rcb_rw, &v);
	if (v != RT_END_OF_RCB)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	rcb->sm_rcb_rw += sizeof(uint32_t);
	sm_buf2uint32(rcb->sm_rcb_base + rcb->sm_rcb_rw, &v);
	sm_buf2uint32(rcb->sm_rcb_base, &tl);
	if (v != tl)
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
#endif /* SM_RCB_END_RCB */

	rcb->sm_rcb_rw = 0;
#if SM_RCB_CHECK
	rcb->sm_rcb_state = SM_RCB_NONE;
#endif
	return SM_SUCCESS;
}

/*
**  SM_RCB_CLOSE_DEC -- Close rcb after decoding data.
**
**	Parameters:
**		rcb -- sm_rcb_P object.
**
**	Returns:
**		SM_SUCCESS
**
**	Last code review: 2005-03-29 21:33:33
**	Last code change:
*/

sm_ret_T
sm_rcb_close_dec(sm_rcb_P rcb)
{
	SM_IS_RCB(rcb);
	SM_REQUIRE(rcb->sm_rcb_base != NULL);
#if SM_RCB_CHECK
	SM_REQUIRE(rcb->sm_rcb_state == SM_RCB_DEC);
	SM_REQUIRE(rcb->sm_rcb_size >= rcb->sm_rcb_len);
	SM_REQUIRE(rcb->sm_rcb_size <= rcb->sm_rcb_max);
#endif
	rcb->sm_rcb_rw = 0;
#if SM_RCB_CHECK
	rcb->sm_rcb_state = SM_RCB_NONE;
#endif
	return SM_SUCCESS;
}

/*
**  SM_RCB_CLOSE_ENC -- Close rcb after encoding data into it.
**
**	Parameters:
**		rcb -- sm_rcb_P object.
**
**	Returns:
**		usual sm_error code; EINVAL (SM_RCB_END_RCB: ENOMEM, etc)
**		(fails only if rcb is improperly used)
**
**	Side Effects: resets state even on error.
**
**	Last code review: 2005-03-22 22:58:10
**	Last code change:
*/

sm_ret_T
sm_rcb_close_enc(sm_rcb_P rcb)
{
	uint32_t len;
#if SM_RCB_END_RCB
	sm_ret_T ret;
#endif

	SM_IS_RCB(rcb);
	SM_REQUIRE(rcb->sm_rcb_base != NULL);
#if SM_RCB_CHECK
	SM_REQUIRE(rcb->sm_rcb_state == SM_RCB_ENC);

	/* reset state even if an error occurs? */
	rcb->sm_rcb_state = SM_RCB_NONE;
#endif /* SM_RCB_CHECK */
	if (rcb->sm_rcb_len <= sizeof(int))
		return sm_error_perm(SM_EM_RECCOM, EINVAL);
	SM_ASSERT(rcb->sm_rcb_len < UINT32_MAX);
#if SM_RCB_END_RCB
	ret = sm_rcb_put3uint32(rcb, 4, RT_END_OF_RCB, rcb->sm_rcb_len + 12);
	if (sm_is_err(ret))
		return ret;
#endif
	len = (uint32_t) (rcb->sm_rcb_len);
	sm_uint32_2buf(len, rcb->sm_rcb_base);
	return SM_SUCCESS;
}

/*
**  SM_RCB_CLOSE_SND -- Close rcb after sending data from it.
**
**	Parameters:
**		rcb -- sm_rcb_P object.
**
**	Returns:
**		SM_SUCCESS
**
**	Last code review: 2005-03-30 22:51:51
**	Last code change:
*/

sm_ret_T
sm_rcb_close_snd(sm_rcb_P rcb)
{
	SM_IS_RCB(rcb);
	SM_REQUIRE(rcb->sm_rcb_base != NULL);
#if SM_RCB_CHECK
	SM_REQUIRE(rcb->sm_rcb_state == SM_RCB_SND);
#endif
	rcb->sm_rcb_rw = 0;
#if SM_RCB_CHECK
	rcb->sm_rcb_state = SM_RCB_NONE;
#endif
	return SM_SUCCESS;
}


syntax highlighted by Code2HTML, v. 0.9.1