/*
 * Copyright (c) 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: ssreplacemsg.c,v 1.13 2007/08/18 15:58:24 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/reccom.h"
#include "sm/rcbst.h"
#include "smtps-str.h"
#include "s2q.h"
#include "smtps.h"
#if MTA_USE_PMILTER
#include "s2m.h"
#include "sm/pmilter.h"
#include "log.h"

/*
**  SS_REPLACEMSG -- read message chunk and write it to cdb
**
**	Parameters:
**		ss_sess -- session context
**		l -- length of buffer to read from rcb
**
**	Returns:
**		usual sm_error code
**
**	Called by: sm_w4q2s_reply()
*/

sm_ret_T
ss_replacemsg(ss_sess_P ss_sess, uint32_t l)
{
	sm_ret_T ret;
	int c;
	uint u;
	ssize_t byteswritten;
	ss_ta_P ss_ta;
	cdb_ctx_P cdb_ctx;
	const uchar *buf;
	static SM_DECL_EOT;

	SM_IS_SS_SESS(ss_sess);
	ss_ta = ss_sess->ssse_ta;
	cdb_ctx = ss_sess->ssse_sctx->ssc_cdb_ctx;

	sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
		SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_INFO, 12,
		"sev=INFO, func=ss_replacemsg, ss_sess=%s, ss_ta=%s, status=%d"
		, ss_sess->ssse_id, ss_ta->ssta_id, ss_ta->ssta_msg_st);
	if (ss_ta->ssta_msg_st != SSTA_ST_MR_CDB_OK)
	{
		ret = sm_rcb_skip(ss_sess->ssse_rcb, l);
		return SM_SUCCESS;
	}

	ret = sm_rcb_getdata(ss_sess->ssse_rcb, &buf, l);
	if (sm_is_err(ret))
	{
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_PMILTER, SS_LMOD_PMILTER,
			SM_LOG_ERROR, 8,
			"sev=ERROR, func=ss_replacemsg, ss_sess=%s, ss_ta=%s, sm_rcb_getdata=%m, l=%d"
			, ss_sess->ssse_id, ss_ta->ssta_id, ret, l);
		ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL;
		return ret;
	}

	for (u = 0; u < l; u++)
	{
		c = buf[u];
		if (c == eot[ss_ta->ssta_eot_state])
		{
			if (++ss_ta->ssta_eot_state >= SM_EOT_LEN)
				break;
		}
		else
		{
			ss_ta->ssta_eot_state = 0;
			if (c == eot[ss_ta->ssta_eot_state])
				++ss_ta->ssta_eot_state;
		}
	}

	/*
	**  Increment u as it is the index in buf[] not the length
	**  of the buffer to write.
	*/

	if (ss_ta->ssta_eot_state >= SM_EOT_LEN && ++u < l)
	{
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_PMILTER, SS_LMOD_PMILTER,
			SM_LOG_ERROR, 8,
			"sev=ERROR, func=ss_replacemsg, found_eot_at=%d, buf_len=%d"
			, u, l);
		l = u;
		ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL;
	}

	ret = cdb_write(cdb_ctx, ss_ta->ssta_msg_fp, buf, l, &byteswritten);
	if (sm_is_err(ret) || byteswritten < 0 || l != (uint32_t) byteswritten)
	{
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_PMILTER, SS_LMOD_PMILTER,
			SM_LOG_ERROR, 8,
			"sev=ERROR, func=ss_replacemsg, ss_sess=%s, ss_ta=%s, cdb_write=%m, len=%u, byteswritten=%d"
			, ss_sess->ssse_id, ss_ta->ssta_id, ret, l
			, (int) byteswritten);
		ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL;
	}
	else
		ss_ta->ssta_rmsg_sz_b += byteswritten;
	return SM_SUCCESS;
}

/*
**  SS_CLOSEMSG -- close (replacement) cdb
**
**	Parameters:
**		ss_sess -- session context
**
**	Returns:
**		usual sm_error code
**
**	Side Effects: removes original cdb if everything goes well here
*/

static sm_ret_T
ss_closemsg(ss_sess_P ss_sess)
{
	sm_ret_T ret;
	ss_ta_P ss_ta;
	cdb_ctx_P cdb_ctx;
	ss_ctx_P ss_ctx;

	SM_IS_SS_SESS(ss_sess);
	ss_ta = ss_sess->ssse_ta;
	SM_IS_SS_TA(ss_ta);
	ss_ctx = ss_sess->ssse_sctx;
	ret = SM_SUCCESS;

	sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
		SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_INFO, 12,
		"sev=INFO, func=ss_closemsg, ss_sess=%s, ss_ta=%s, status=%d"
		, ss_sess->ssse_id, ss_ta->ssta_id, ss_ta->ssta_msg_st);

	/* no message has been written? */
	if (SSTA_ST_MR_NONE == ss_ta->ssta_msg_st)
		return SM_SUCCESS;

	cdb_ctx = ss_sess->ssse_sctx->ssc_cdb_ctx;
	if (NULL == ss_ta->ssta_cdb_id)
	{
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 2,
			"sev=ERR, func=ss_closemsg, ss_sess=%s, ss_ta=%s, status=missing cdb id"
			, ss_sess->ssse_id, ss_ta->ssta_id);
		ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL;
		ret = sm_error_perm(SM_EM_PMILTER, SM_E_UNEXPECTED);
		goto error;
	}

	if (SM_GOT_CRLF == ss_ta->ssta_eot_state)
	{
		const char buf[] = ".\r\n";
		size_t l;
		ssize_t byteswritten;

		l = sizeof(buf) - 1;
		ret = cdb_write(cdb_ctx, ss_ta->ssta_msg_fp,
				(const uchar *) buf, l, &byteswritten);
		if (sm_is_err(ret) || byteswritten < 0 ||
		    l != (size_t) byteswritten)
		{
			sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
				SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 2,
				"sev=ERR, func=ss_closemsg, ss_sess=%s, ss_ta=%s, cdb_write=%m, len=%y, byteswritten=%d, status=fail"
				, ss_sess->ssse_id, ss_ta->ssta_id
				, ret, l, byteswritten);
			ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL;
		}
		else
			ss_ta->ssta_rmsg_sz_b += byteswritten;
	}
	else if (ss_ta->ssta_eot_state < SM_EOT_LEN)
	{
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 2,
			"sev=ERR, func=ss_closemsg, ss_sess=%s, ss_ta=%s, eot_state=%d, status=invalid end of message"
			, ss_sess->ssse_id, ss_ta->ssta_id
			, ss_ta->ssta_eot_state);
		ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL;
		ret = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
	}

	if (SSTA_ST_MR_CDB_OK != ss_ta->ssta_msg_st)
		goto error;

	ret = cdb_close(cdb_ctx, ss_ta->ssta_msg_fp, SM_IO_CF_SYNC);
	if (sm_is_err(ret))
	{
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 8,
			"sev=ERR, func=ss_closemsg, ss_sess=%s, ss_ta=%s, cdb_close=%m"
			, ss_sess->ssse_id, ss_ta->ssta_id, ret);
		goto error;
	}
	ss_ta->ssta_msg_fp = NULL;
	ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_CLSD;

	ret = sm_s2m_msg_rplc_stat(ss_sess, ss_ctx->ssc_s2m_ctx,
			ss_sess->ssse_id, SM_SUCCESS);

	if (sm_is_err(ret))
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_WARN, 9,
			"sev=WARN, func=ss_closemsg, ss_sess=%s, ss_ta=%s, sm_s2m_msg_rplc_stat=%#x"
			, ss_sess->ssse_id, ss_ta->ssta_id, ret);

	/* remove original file, new file is safely stored */
	(void) cdb_abort(cdb_ctx, ss_ta->ssta_dfp);
	ss_ta->ssta_dfp = NULL;
	SSTA_CLR_FLAG(ss_ta, SSTA_FL_CDB_EXISTS);
	ss_ta->ssta_msg_sz_b = ss_ta->ssta_rmsg_sz_b;

	sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
		SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_INFO, 9,
		"sev=INFO, func=ss_closemsg, ss_sess=%s, ss_ta=%s, status=message_replaced, size=%lu"
		, ss_sess->ssse_id, ss_ta->ssta_id
		, (ulong) ss_ta->ssta_msg_sz_b);
	return SM_SUCCESS;

  error:
	if (cdb_ctx != NULL)
	{
		if (ss_ta->ssta_msg_fp != NULL)
		{
			(void) cdb_abort(cdb_ctx, ss_ta->ssta_msg_fp);
			ss_ta->ssta_msg_fp = NULL;

			/* abort also removes the file */
			SM_CSTR_FREE(ss_ta->ssta_cdb_id);
		}
		if (ss_ta->ssta_cdb_id != NULL)
		{
			(void) cdb_unlink(cdb_ctx,
				(const char *)
					sm_cstr_data(ss_ta->ssta_cdb_id));
			SM_CSTR_FREE(ss_ta->ssta_cdb_id);
		}
	}
	if (!sm_is_err(ret))
		ret = sm_err_perm(EIO);

	(void) sm_s2m_msg_rplc_stat(ss_sess, ss_ctx->ssc_s2m_ctx,
			ss_sess->ssse_id, ret);
	return ret;
}

/*
**  SS_RPLCMSG -- read message chunks and write them to cdb
**
**	Parameters:
**		ss_sess -- session context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
ss_rplcmsg(ss_sess_P ss_sess)
{
	sm_ret_T ret;
	ss_ta_P ss_ta;
	ss_ctx_P ss_ctx;
	cdb_ctx_P cdb_ctx;

	ss_ta = ss_sess->ssse_ta;
	SM_IS_SS_TA(ss_ta);
	ss_ctx = ss_sess->ssse_sctx;
	cdb_ctx = ss_sess->ssse_sctx->ssc_cdb_ctx;

	ret = cdb_open_write(cdb_ctx, ss_ta->ssta_id, ss_ta->ssta_msg_fp, 
			SM_IO_WREXCL, 1, &ss_ta->ssta_cdb_id);
	if (sm_is_err(ret))
	{
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERROR, 8,
			"sev=ERROR, func=ss_rplcmsg, ss_sess=%s, ss_ta=%s, cdb_open=%m"
			, ss_sess->ssse_id, ss_ta->ssta_id, ret);
		ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_NONE;
	}
	else
		ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_OK;
	ss_ta->ssta_eot_state = SM_GOT_CRLF;
	do
	{
		ret = ss_add_rq(ss_sess, ss_sess->ssse_id,
				ss_ctx->ssc_s2m_ctx, NULL);
		if (sm_is_err(ret))
		{
			sm_log_write(ss_ctx->ssc_lctx,
				SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 4,
				"sev=ERR, func=ss_rplcmsg, ss_sess=%s, ss_ta=%s, ss_add_rq=%m"
				, ss_sess->ssse_id, ss_ta->ssta_id
				, ret);
			ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL;
			break;
		}
		ret = sm_w4q2s_reply(ss_sess,
			ss_ctx->ssc_cnf.ss_cnf_w4m2s,
			ss_ctx->ssc_s2m_ctx);
		if (sm_is_err(ret))
		{
			/* don't use milter anymore in this session */
			SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE);
			sm_log_write(ss_ctx->ssc_lctx,
				SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 4,
				"sev=ERR, func=ss_rplcmsg, ss_sess=%s, ss_ta=%s, sm_w4q2s_reply=%m"
				, ss_sess->ssse_id, ss_ta->ssta_id
				, ret);
			ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL;
			break;
		}
	} while (SMTP_R_CONT == ret);
	ret = ss_closemsg(ss_sess);
	if (sm_is_err(ret))
	{
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERROR, 6,
			"sev=ERROR, func=ss_rplcmsg, ss_sess=%s, ss_ta=%s, ss_closemsg=%m"
			, ss_sess->ssse_id, ss_ta->ssta_id, ret);

		if (SSC_IS_MFLAG(ss_ctx, SSC_MFL_PM_421))
			ret = SMTP_R_SSD;
		else	/* ignore error */
			ret = SM_SUCCESS;
	}
	return ret;
}
#endif /* MTA_USE_PMILTER */


syntax highlighted by Code2HTML, v. 0.9.1