/*
 * Copyright (c) 2004-2006 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.
 */

/*
**  SMTPS - policy milter communication module
*/

#include "sm/generic.h"
SM_RCSID("@(#)$Id: s2m.c,v 1.32 2007/10/23 03:57:40 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/reccom.h"
#include "sm/smar.h"
#include "statethreads/st.h"
#include "sm/str.h"
#include "sm/rcbst.h"
#include "sm/stthreads.h"
#include "smtps-str.h"
#include "s2q.h"
#include "smtps.h"
#include "s2m.h"

#if MTA_USE_PMILTER

/*
**  SM_NEW_MACV -- create string for macro
**
**	Parameters:
**		ss_sess -- session context
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_new_macv(ss_sess_P ss_sess)
{
	SM_REQUIRE(ss_sess != NULL);
	if (NULL == ss_sess->ssse_macv)
		ss_sess->ssse_macv = sm_str_new(NULL, 32, 256);
	if (NULL != ss_sess->ssse_macv) {
		sm_str_clr(ss_sess->ssse_macv);
		return SM_SUCCESS;
	}
	return sm_err_temp(ENOMEM);
}

/*
**  SM_SS_GETMACRO -- get value of macro
**
**	Parameters:
**		ss_sess -- session context
**		stage -- stage of the SMTP dialogue
**		macm -- name of macro
**		pmacv -- (pointer to) value of macro (output)
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_ss_getmacro(ss_sess_P ss_sess, uint stage, uint32_t macm, sm_str_P *pmacv)
{
	sm_ret_T ret;
	ss_ctx_P ss_ctx;
	ss_ta_P ss_ta;

	SM_REQUIRE(pmacv != NULL);
	SM_IS_SS_SESS(ss_sess);
	ss_ctx = ss_sess->ssse_sctx;
	SM_IS_SS_CTX(ss_ctx);
	*pmacv = NULL;
	ret = SM_SUCCESS;
	ss_ta = ss_sess->ssse_ta;

	switch (macm) {
	  case PMM_SRVHOSTNAME:
		*pmacv = ss_ctx->ssc_hostname;
		break;

	  case PMM_SEID:	/* could be "resolved" in libpmilter */
		break;
	  case PMM_CLIENT_RESOLVE:
		if (ss_sess != NULL) {
			ret = sm_new_macv(ss_sess);
			if (sm_is_err(ret))
				return ret;
			sm_str_scat(ss_sess->ssse_macv,
				sm_rslv2txt(ss_sess->ssse_cltrslv));
			*pmacv = ss_sess->ssse_macv;
		}
		break;

#if MTA_USE_TLS
	  case PMM_TLS_VERSION:
		if (ss_sess != NULL && ss_sess->ssse_tlsi != NULL)
			*pmacv = ss_sess->ssse_tlsi->tlsi_version;
		break;
	  case PMM_TLS_CIPHER_SUITE:
		if (ss_sess != NULL)
			*pmacv = ss_sess->ssse_tlsi->tlsi_cipher;
		break;
	  case PMM_TLS_CIPHER_BITS:
		if (ss_sess != NULL && ss_sess->ssse_tlsi != NULL) {
			ret = sm_new_macv(ss_sess);
			if (sm_is_err(ret))
				return ret;
			sm_strprintf(ss_sess->ssse_macv, "%d",
				ss_sess->ssse_tlsi->tlsi_cipher_bits);
			*pmacv = ss_sess->ssse_macv;
		}
		break;
	  case PMM_TLS_ALG_BITS:
		if (ss_sess != NULL && ss_sess->ssse_tlsi != NULL) {
			ret = sm_new_macv(ss_sess);
			if (sm_is_err(ret))
				return ret;
			sm_strprintf(ss_sess->ssse_macv, "%d",
				ss_sess->ssse_tlsi->tlsi_algs_bits);
			*pmacv = ss_sess->ssse_macv;
		}
		break;
	  case PMM_TLS_VRFY:
		if (ss_sess != NULL && ss_sess->ssse_tlsi != NULL) {
			ret = sm_new_macv(ss_sess);
			if (sm_is_err(ret))
				return ret;
			sm_str_scat(ss_sess->ssse_macv,
				tls_vrfy2txt(ss_sess->ssse_tlsi->tlsi_vrfy));
			*pmacv = ss_sess->ssse_macv;
		}
		break;
	  case PMM_TLS_CERT_SUBJECT:
		if (ss_sess != NULL && ss_sess->ssse_tlsi != NULL)
			*pmacv = ss_sess->ssse_tlsi->tlsi_cert_subject;
		break;
	  case PMM_TLS_CERT_ISSUER:
		if (ss_sess != NULL && ss_sess->ssse_tlsi != NULL)
			*pmacv = ss_sess->ssse_tlsi->tlsi_cert_issuer;
		break;
	  case PMM_TLS_CN_SUBJECT:
		if (ss_sess != NULL && ss_sess->ssse_tlsi != NULL)
			*pmacv = ss_sess->ssse_tlsi->tlsi_cn_subject;
		break;
	  case PMM_TLS_CN_ISSUER:
		if (ss_sess != NULL && ss_sess->ssse_tlsi != NULL)
			*pmacv = ss_sess->ssse_tlsi->tlsi_cn_issuer;
		break;
#endif /* MTA_USE_TLS */

#if MTA_USE_SASL
	  case PMM_AUTH_TYPE:
		if (ss_sess != NULL)
			*pmacv = ss_sess->ssse_sasl_mech;
		break;
	  case PMM_AUTH_AUTHEN:
		if (ss_sess != NULL)
			*pmacv = ss_sess->ssse_sasl_authen;
		break;
	  case PMM_AUTH_AUTHOR:
		if (ss_sess != NULL)
			*pmacv = ss_sess->ssse_sasl_author;
		break;
#endif /* MTA_USE_SASL */

	  case PMM_MAIL_TAID:	/* could be "resolved" in libpmilter? */
		break;
	  case PMM_DOT_HOPS:
		if (ss_ta != NULL) {
			ret = sm_new_macv(ss_sess);
			if (sm_is_err(ret))
				return ret;
			sm_strprintf(ss_sess->ssse_macv, "%u",
				ss_ta->ssta_hops);
			*pmacv = ss_sess->ssse_macv;
		}
		break;
	  case PMM_DOT_MSGID:
		if (ss_ta != NULL)
			*pmacv = ss_ta->ssta_msgid;
		break;

	  default:
		/* ignore errors for now */
		/* ret = sm_err_perm(SM_E_NOTFOUND); */
		break;
	}
	return ret;
}

/*
**  SM_S2M_MACROS -- send macros to PMILTER
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		stage -- which stage of the SMTP dialogue?
**		rcb -- RCB to fill in
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_s2m_macros(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, uint stage, sm_rcb_P rcb)
{
	sm_ret_T ret;
	uint j;
	uint32_t macm;
	sm_str_P macv;
	ss_ctx_P ss_ctx;

	ss_ctx = ss_sess->ssse_sctx;
	SM_ASSERT(stage < PM_MAX_MACROS);
	SSQ_DPRINTF((smioerr,
		"sev=DBG, func=sm_s2m_macros, stage=%u, mac=%#X\n", stage, ss_ctx->ssc_mac_names[stage][0]));
	if (ss_ctx->ssc_mac_names[stage][0] == PMM_END)
		return SM_SUCCESS;
	ret = SM_SUCCESS;
	for (j = 0;
	     !sm_is_err(ret) && j < PM_MAX_MACROS &&
	     (macm = ss_ctx->ssc_mac_names[stage][j]) != PMM_END;
	     j++)
	{
		ret = sm_ss_getmacro(ss_sess, stage, macm, &macv);
		if (sm_is_err(ret))
			return ret;
		if (macv != NULL) {
			SSQ_DPRINTF((smioerr,
				"sev=DBG, func=sm_s2m_macros, macm=%#X, macv=%S\n", macm, macv));
			ret = sm_rcb_putv(rcb, 0,
				SM_RCBV_INT, RT_S2M_MACM, macm,
				SM_RCBV_STR, RT_S2M_MACV, macv,
				SM_RCBV_END);
			if (sm_is_err(ret))
				return ret;
		}
	}
	return ret;
}

/*
**  SM_S2M_CLT -- send client address to PMILTER
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		sid -- session id
**		cltipv4 -- client IPv4 address
**		port -- port of server
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_s2m_clt(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, sessta_id_P sid, uint32_t cltipv4, ushort port)
{
	sm_ret_T ret;
	sm_rcb_P rcb;

	SM_REQUIRE(s2m_ctx != NULL);
	SM_IS_SS_SESS(ss_sess);
	S2Q_CHK_CONN_R(ss_sess, s2m_ctx);

	rcb = ss_sess->ssse_rcb;
	ret = sm_rcb_putrec(rcb, RCB_PUTR_FIRST|RCB_PUTR_OPEN, 0, -1,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_S2M_ID, s2m_ctx->s2q_ss_ctx->ssc_id,
		SM_RCBV_BUF, RT_S2M_NSEID, sid, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_S2M_IPV4, cltipv4,
		SM_RCBV_INT, RT_S2M_PORT, port,
		SM_RCBV_CSTR, RT_S2M_HOST, ss_sess->ssse_cltname,
		SM_RCBV_END);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_clt, sess=%p, sm_rcb_putrec=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_s2m_macros(ss_sess, s2m_ctx, PM_SMST_CONNECT, rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_clt, sess=%p, sm_s2m_macros=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_rcb_close_enc(rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_clt, sess=%p, sm_rcb_close_enc=%m\n",
			ss_sess, ret));
		goto error;
	}

	ret = ss_send_rq(ss_sess, sid, s2m_ctx, rcb, true);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_clt, sess=%p, ss_send_rq=%m\n",
			ss_sess, ret));
		goto error;
	}
	return SM_SUCCESS;

  error:
	return ret;
}

/*
**  SM_S2M_HELO -- send HELO string to PMILTER
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		sid -- session id
**		ehlo -- was EHLO used?
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_s2m_helo(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, sessta_id_P sid, bool ehlo)
{
	sm_ret_T ret;
	sm_rcb_P rcb;

	SM_REQUIRE(s2m_ctx != NULL);
	SM_IS_SS_SESS(ss_sess);
	S2Q_CHK_CONN_R(ss_sess, s2m_ctx);

	rcb = ss_sess->ssse_rcb;
	ret = sm_rcb_putrec(rcb, RCB_PUTR_FIRST|RCB_PUTR_OPEN, 0, -1,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_S2M_ID, s2m_ctx->s2q_ss_ctx->ssc_id,
		SM_RCBV_BUF, RT_S2M_SEID, sid, SMTP_STID_SIZE,
		SM_RCBV_STR, ehlo ? RT_S2M_EHLO : RT_S2M_HELO,
			ss_sess->ssse_helo,
		SM_RCBV_END);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_helo, sess=%p, sm_rcb_putrec=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_s2m_macros(ss_sess, s2m_ctx, PM_SMST_EHLO, rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_helo, sess=%p, sm_s2m_macros=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_rcb_close_enc(rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_helo, sess=%p, sm_rcb_close_enc=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = ss_send_rq(ss_sess, sid, s2m_ctx, rcb, true);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_help, sess=%p, ss_send_rq=%m\n",
			ss_sess, ret));
		goto error;
	}
	return SM_SUCCESS;

  error:
	return ret;
}

/*
**  SM_S2M_MAIL -- send mail address to PMILTER
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		sid -- session id
**		tid -- transaction id
**		mail -- mail from address
**		argoffset -- offset of arguments in sess_rd (0: no args)
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_s2m_mail(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, sessta_id_P sid, sessta_id_P tid, sm_str_P mail, uint argoffset)
{
	sm_ret_T ret;
	sm_rcb_P rcb;

	SM_REQUIRE(s2m_ctx != NULL);
	SM_IS_SS_SESS(ss_sess);
	S2Q_CHK_CONN_R(ss_sess, s2m_ctx);
	if (argoffset > 0)
		SM_REQUIRE(sm_str_getlen(ss_sess->ssse_rd) > argoffset);

	rcb = ss_sess->ssse_rcb;
	ret = sm_rcb_putrec(rcb, RCB_PUTR_FIRST|RCB_PUTR_OPEN, 0, -1,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_S2M_ID, s2m_ctx->s2q_ss_ctx->ssc_id,
		SM_RCBV_BUF, RT_S2M_SEID, sid, SMTP_STID_SIZE,
		SM_RCBV_BUF, RT_S2M_NTAID, tid, SMTP_STID_SIZE,
		SM_RCBV_STR, RT_S2M_MAIL, mail,
		SM_RCBV_BUF, (argoffset > 0) ? RT_S2M_ARG : RT_NOSEND,
			sm_str_getdata(ss_sess->ssse_rd) + argoffset,
			sm_str_getlen(ss_sess->ssse_rd) - argoffset,
		SM_RCBV_END);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_mail, sess=%p, sm_rcb_putrec=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_s2m_macros(ss_sess, s2m_ctx, PM_SMST_MAIL, rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_mail, sess=%p, sm_s2m_macros=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_rcb_close_enc(rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_mail, sess=%p, sm_rcb_close_enc=%m\n",
			ss_sess, ret));
		goto error;
	}

	ret = ss_send_rq(ss_sess, sid, s2m_ctx, rcb, true);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_mail, sess=%p, ss_send_rq=%m\n",
			ss_sess, ret));
		goto error;
	}
	return SM_SUCCESS;

  error:
	return ret;
}

/*
**  SM_S2M_RCPT -- send recipient to PMILTER
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		sid -- session id
**		rcpt_idx -- rcpt idx
**		rcpt -- rcpt to address
**		cur_status -- current status while dealing with RCPT
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_s2m_rcpt(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, sessta_id_P sid, int rcpt_idx, sm_str_P rcpt, sm_ret_T cur_status)
{
	sm_ret_T ret;
	sm_rcb_P rcb;

	SM_REQUIRE(s2m_ctx != NULL);
	SM_IS_SS_SESS(ss_sess);
	S2Q_CHK_CONN_R(ss_sess, s2m_ctx);

	rcb = ss_sess->ssse_rcb;
	ret = sm_rcb_putrec(rcb, RCB_PUTR_FIRST|RCB_PUTR_OPEN, 0, -1,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_S2M_ID, (uint32_t) s2m_ctx->s2q_ss_ctx->ssc_id,
		SM_RCBV_BUF, RT_S2M_SEID, sid, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_S2M_RCPT_IDX, (uint32_t) rcpt_idx,
		SM_RCBV_STR, RT_S2M_RCPT, rcpt,
		SM_RCBV_INT, RT_S2M_RCPT_ST, (uint32_t) cur_status,
		SM_RCBV_END);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_rcpt, sess=%p, sm_rcb_putrec=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_s2m_macros(ss_sess, s2m_ctx, PM_SMST_RCPT, rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_rcpt, sess=%p, sm_s2m_macros=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_rcb_close_enc(rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_rcpt, sess=%p, sm_rcb_close_enc=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = ss_send_rq(ss_sess, sid, s2m_ctx, rcb, true);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_rcpt, sess=%p, ss_send_rq=%m\n",
			ss_sess, ret));
		goto error;
	}
	return SM_SUCCESS;

  error:
	return ret;
}

/*
**  SM_S2M_DATA -- inform PMILTER about DATA command
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		sid -- session id
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_s2m_data(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, sessta_id_P sid)
{
	sm_ret_T ret;
	sm_rcb_P rcb;

	SM_REQUIRE(s2m_ctx != NULL);
	SM_IS_SS_SESS(ss_sess);
	S2Q_CHK_CONN_R(ss_sess, s2m_ctx);

	rcb = ss_sess->ssse_rcb;
	ret = sm_rcb_putrec(rcb, RCB_PUTR_FIRST|RCB_PUTR_OPEN, 0, -1,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_S2M_ID, (uint32_t) s2m_ctx->s2q_ss_ctx->ssc_id,
		SM_RCBV_BUF, RT_S2M_SEID, sid, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_S2M_DATA, 0,
		SM_RCBV_END);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_data, sess=%p, sm_rcb_putrec=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_s2m_macros(ss_sess, s2m_ctx, PM_SMST_DATA, rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_data, sess=%p, sm_s2m_macros=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = sm_rcb_close_enc(rcb);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_data, sess=%p, sm_rcb_close_enc=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = ss_send_rq(ss_sess, sid, s2m_ctx, rcb, true);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_data, sess=%p, ss_send_rq=%m\n",
			ss_sess, ret));
		goto error;
	}
	return SM_SUCCESS;

  error:
	return ret;
}

/*
**  SM_S2M_MSG -- send message chunk to PMILTER
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		sid -- session id
**		buf -- message chunk
**		len -- length of message chunk
**		flags -- some flags
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_s2m_msg(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, sessta_id_P sid, uchar *buf, size_t len, uint flags)
{
	sm_ret_T ret;
	sm_rcb_P rcb;

	if (len == 0)
		return SM_SUCCESS;
	SM_REQUIRE(s2m_ctx != NULL);
	SM_IS_SS_SESS(ss_sess);
	S2Q_CHK_CONN_R(ss_sess, s2m_ctx);
	SM_REQUIRE(buf != NULL);

	rcb = ss_sess->ssse_rcb;
	ret = sm_rcb_putrec(rcb,
		SM_IS_FLAG(flags, SM_S2M_MSG_LAST)
			? (RCB_PUTR_FIRST|RCB_PUTR_OPEN) : RCB_PUTR_DFLT,
		0, -1,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_S2M_ID, (uint32_t) s2m_ctx->s2q_ss_ctx->ssc_id,
		SM_RCBV_BUF, RT_S2M_SEID, sid, SMTP_STID_SIZE,
		SM_RCBV_BUF, SM_IS_FLAG(flags, SM_S2M_MSG_LAST)
				? RT_S2M_DOT : RT_S2M_MSG, buf, len,
		SM_RCBV_END);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_msg, sess=%p, sm_rcb_putrec=%m\n",
			ss_sess, ret));
		goto error;
	}
	if (SM_IS_FLAG(flags, SM_S2M_MSG_LAST)) {
		ret = sm_s2m_macros(ss_sess, s2m_ctx, PM_SMST_DOT, rcb);
		if (sm_is_err(ret)) {
			SSQ_DPRINTF((smioerr,
				"sev=ERROR, func=sm_s2m_msg, sess=%p, sm_s2m_macros=%m\n",
				ss_sess, ret));
			goto error;
		}
		ret = sm_rcb_close_enc(rcb);
		if (sm_is_err(ret)) {
			SSQ_DPRINTF((smioerr,
				"sev=ERROR, func=sm_s2m_msg, sess=%p, sm_rcb_close_enc=%m\n",
				ss_sess, ret));
			goto error;
		}
	}
	ret = ss_send_rq(ss_sess, sid, s2m_ctx, rcb,
			SM_IS_FLAG(flags, SM_S2M_MSG_REPLY));
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_msg, sess=%p, ss_send_rq=%m\n",
			ss_sess, ret));
		goto error;
	}
	return SM_SUCCESS;

  error:
	return ret;
}

/*
**  SM_S2M_MSG_RPLC_STAT -- tell PMILTER about status of message replacement
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		sid -- session id
**		cur_status -- status of message replacement
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_s2m_msg_rplc_stat(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, sessta_id_P sid, sm_ret_T cur_status)
{
	sm_ret_T ret;
	sm_rcb_P rcb;

	SM_REQUIRE(s2m_ctx != NULL);
	SM_IS_SS_SESS(ss_sess);
	S2Q_CHK_CONN_R(ss_sess, s2m_ctx);

	rcb = ss_sess->ssse_rcb;
	ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_S2M_ID, (uint32_t) s2m_ctx->s2q_ss_ctx->ssc_id,
		SM_RCBV_BUF, RT_S2M_SEID, sid, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_S2M_MSG_RPLC_STAT, cur_status,
		SM_RCBV_END);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_msg_rplc_stat, sess=%p, sm_rcb_putrec=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = ss_send_rq(ss_sess, sid, s2m_ctx, rcb, false);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_msg_rplc_stat, sess=%p, ss_send_rq=%m\n",
			ss_sess, ret));
		goto error;
	}
	return SM_SUCCESS;

  error:
	return ret;
}

/*
**  SM_S2M_ABORT -- tell PMILTER to abort current transaction
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		sid -- session id
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_s2m_abort(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, sessta_id_P sid)
{
	sm_ret_T ret;
	sm_rcb_P rcb;

	SM_REQUIRE(s2m_ctx != NULL);
	SM_IS_SS_SESS(ss_sess);
	S2Q_CHK_CONN_R(ss_sess, s2m_ctx);

	rcb = ss_sess->ssse_rcb;
	ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_S2M_ID, (uint32_t) s2m_ctx->s2q_ss_ctx->ssc_id,
		SM_RCBV_BUF, RT_S2M_SEID, sid, SMTP_STID_SIZE,
		SM_RCBV_INT, RT_S2M_ABORT_TA, 0,
		SM_RCBV_END);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_abort, sess=%p, sm_rcb_putrec=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = ss_send_rq(ss_sess, sid, s2m_ctx, rcb, false);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_abort, sess=%p, ss_send_rq=%m\n",
			ss_sess, ret));
		goto error;
	}
	return SM_SUCCESS;

  error:
	return ret;
}

/*
**  SM_S2M_CSEID -- inform PMILTER that session is closed
**
**	Parameters:
**		ss_sess -- session context
**		s2m_ctx -- S2M context
**		sid -- session id
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sm_s2m_cseid(ss_sess_P ss_sess, s2q_ctx_P s2m_ctx, sessta_id_P sid)
{
	sm_ret_T ret;
	sm_rcb_P rcb;

	SM_REQUIRE(s2m_ctx != NULL);
	SM_IS_SS_SESS(ss_sess);
	S2Q_CHK_CONN_R(ss_sess, s2m_ctx);

	rcb = ss_sess->ssse_rcb;
	ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
		SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
		SM_RCBV_INT, RT_S2M_ID, s2m_ctx->s2q_ss_ctx->ssc_id,
		SM_RCBV_BUF, RT_S2M_CSEID, sid, SMTP_STID_SIZE,
		SM_RCBV_END);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_cseid, sess=%p, sm_rcb_putrec=%m\n",
			ss_sess, ret));
		goto error;
	}
	ret = ss_send_rq(ss_sess, sid, s2m_ctx, rcb, false);
	if (sm_is_err(ret)) {
		SSQ_DPRINTF((smioerr,
			"sev=ERROR, func=sm_s2m_cseid, sess=%p, ss_send_rq=%m\n",
			ss_sess, ret));
		goto error;
	}
	return SM_SUCCESS;

  error:
	return ret;
}
#endif /* MTA_USE_PMILTER */


syntax highlighted by Code2HTML, v. 0.9.1