/*
 * Copyright (c) 2005, 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.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: pmilter.c,v 1.34 2007/08/18 15:58:24 ca Exp $")

#include "sm/assert.h"
#include "sm/error.h"
#include "sm/io.h"
#include "sm/str.h"
#include "sm/types.h"
#include "sm/ctype.h"
#include "sm/smar.h"

#include "s2q.h"
#include "smtps.h"
#include "s2m.h"
#include "log.h"
#include "pmilter.h"

#if MTA_USE_PMILTER

# if MTA_USE_RSAD
/*
**  SSPM_CLRREPLIES -- Clear replies in transaction context
**
**	Parameters:
**		ss_ta -- SMTP server transaction context
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
sspm_clrreplies(ss_ta_P ss_ta)
{
	SM_IS_SS_TA(ss_ta);
	if (0 == ss_ta->ssta_nreplies)
		return SM_SUCCESS;

	if (ss_ta->ssta_rcodes != NULL)
		SM_FREE(ss_ta->ssta_rcodes);

	if (ss_ta->ssta_replies != NULL) {
		uint u;
		sm_str_P reply;

		for (u = 0; u < ss_ta->ssta_nreplies; u++) {
			reply = ss_ta->ssta_replies[u];
			SM_STR_FREE(reply);
		}
		SM_FREE(ss_ta->ssta_replies);
	}
	ss_ta->ssta_nreplies = 0;

	return SM_SUCCESS;
}
# endif /* MTA_USE_RSAD */

/*
**  SSPM_CHK_REPLY -- check whether SMTP reply text is "OK" and fix it if not
**
**	Parameters:
**		replystr -- str for SMTP reply text (in/out)
**		replycode -- error code to transform
**		phase -- SMTP phase
**
**	Returns:
**		usual return code
*/

static sm_ret_T
sspm_chk_reply(sm_str_P replystr, sm_ret_T replycode, int phase)
{
	uint len;

	if (replystr == NULL)
		return SM_SUCCESS;

	len = sm_str_getlen(replystr);
	if (len < 5) {
		(void) ss_crt_reply(replystr, replycode, phase, true);
		return sm_err_temp(EINVAL);
	}

	/* check for \r\n */
	if (sm_str_rd_elem(replystr, len - 1) != '\n' ||
	    sm_str_rd_elem(replystr, len - 2) != '\r')
	{
		sm_str_scat(replystr, "\r\n");
		return sm_err_temp(EINVAL);
	}

	return SM_SUCCESS;
}

/*
**  SSPM_HELO -- Inform libpmilter about EHLO/HELO command
**
**	Parameters:
**		ss_sess -- SMTPS session
**		ehlo -- was EHLO used?
**
**	Returns:
**		>0: reply code to use for EHLO
**		<=0: usual sm_error code: SM_SUCCESS
*/

sm_ret_T
sspm_helo(ss_sess_P ss_sess, bool ehlo)
{
	sm_ret_T ret;
	ss_ta_P ss_ta;

	SM_IS_SS_SESS(ss_sess);
	ss_ta = ss_sess->ssse_ta;
	SM_IS_SS_TA(ss_ta);

	if (!(SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE)
	    && SSC_IS_PMCAP(ss_sess->ssse_sctx, SM_SCAP_PM_EHLO)))
		return SM_SUCCESS;

	sm_str_clr(ss_sess->ssse_wr);
	ret = sm_s2m_helo(ss_sess, ss_sess->ssse_sctx->ssc_s2m_ctx,
			ss_sess->ssse_id, ehlo);
	if (sm_is_err(ret)) {
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERROR, 7,
			"sev=ERROR, func=sspm_ehlo, ss_sess=%s, sm_s2m_ehlo=%m"
			, ss_sess->ssse_id, ret);
	}
	else {
		SSTA_SET_FLAG(ss_ta, SSTA_FL_PM_CALLED);
		SSSE_SET_FLAG(ss_sess, SSSE_FL_PM_CALLED);
		ret = sm_w4q2s_reply(ss_sess, ss_sess->ssse_sctx->ssc_cnf.ss_cnf_w4m2s,
			ss_sess->ssse_sctx->ssc_s2m_ctx);
	}
	if (sm_is_err(ret)) {
		SSPM_TRY_AGAIN(ss_sess->ssse_sctx, ss_sess);
		SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE);
		if (SSC_IS_MFLAG(ss_sess->ssse_sctx, SSC_MFL_PM_421))
			ret = SMTP_R_SSD;
		else
			ret = SM_SUCCESS;
	}
#if 0
	/* ??? this can't happen, ret is reset above */
	if (sm_is_err(ret)) {
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERROR, 6,
			"sev=ERROR, func=sspm_ehlo, ss_sess=%s, sm_w4q2s_reply=%m"
			, ss_sess->ssse_id, ret);
/* XXX properly deal with error... */
/* write a macro? */
		goto error;
	}
	else
#endif
	if (SMAR_RISQUICK(ret)) {
		if (SMTP_R_ACCEPT == ret)
			SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE);
#if 0
		SSTA_SET_FLAG(ss_ta, SSTA_FL_EHLO_QCK);
#endif
		SMAR_RCLRQUICK(ret);
	}
	else if (SSSE_IS_CFLAG(ss_sess, SSSE_CFL_DELAY_CHKS) &&
		 IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret))
	{
		sm_str_P str;

		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_DEBUG, 12,
			"sev=DBG, func=sspm_ehlo, ss_sess=%s, reply=%d, text=%@T"
			, ss_sess->ssse_id, ret, ss_sess->ssse_wr);

		if (sm_str_getlen(ss_sess->ssse_wr) != 0) {
			str = sm_str_dup(NULL, ss_sess->ssse_wr);
			(void) sspm_chk_reply(str, ret, SS_PHASE_EHLO);
		}
		else
			str = NULL;
		if (str != NULL || sm_str_getlen(ss_sess->ssse_wr) == 0) {
			if (!IS_SMTP_REPLY(ss_sess->ssse_acc.ssa_reply_code)) {
				ss_sess->ssse_acc.ssa_map_result = SM_ACC_FOUND;
				ss_sess->ssse_acc.ssa_reply_text = str;
				ss_sess->ssse_acc.ssa_reply_code = ret;
			}
			/* ss_sess->ssse_ehlo_acc.ssa_reply_text = str; */
			sm_str_clr(ss_sess->ssse_wr);
			ret = SMTP_R_OK;
		}
		else {
			/* can't accept session: ENOMEM */
			ret = SMTP_R_SSD;
			(void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_EHLO, true);
			sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
				SS_LCAT_SERVER, SS_LMOD_SERVER,
				SM_LOG_ERROR, 4,
				"sev=ERROR, func=sspm_ehlo, ss_sess=%s, sm_str_dup=ENOMEM"
				, ss_sess->ssse_id);
		}
		SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE);
	}
	if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) {
		int temprc;

		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_INFO, 10,
			"sev=INFO, func=sspm_ehlo, ss_sess=%s, ehlo=%N, stat=%r, text=%@T"
			, ss_sess->ssse_id, ss_sess->ssse_str
			, ret, ss_sess->ssse_wr);
		if (!SMTP_REPLY_MATCHES_RCODE(ss_sess->ssse_wr, ret, 0, temprc)) {
			/* overwrite non-matching text */
			(void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_EHLO, true);
		}
		else
			(void) sspm_chk_reply(ss_sess->ssse_wr, ret, SS_PHASE_EHLO);
	}
	else {
		sm_log_write(ss_sess->ssse_sctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_INFO, 13,
			"sev=INFO, func=sspm_ehlo, ss_sess=%s, ss_ta=%s, sm_w4q2s_reply_ar=%m"
			, ss_sess->ssse_id, ss_ta->ssta_id, ret);
		if (SMTP_R_CONT == ret)
			ret = SM_SUCCESS;	/* "normalize" */
	}
	return ret;
}

/*
**  SSPM_MAIL -- Inform libpmilter about MAIL command
**
**	Parameters:
**		ss_sess -- SMTPS session
**		cur_status -- current status while dealing with MAIL
**		argoffset -- where do arguments start?
**
**	Returns:
**		>0: reply code to use for MAIL
**		<=0: usual sm_error code: SM_SUCCESS
*/

sm_ret_T
sspm_mail(ss_sess_P ss_sess, sm_ret_T cur_status, uint argoffset)
{
	sm_ret_T ret;
	ss_ta_P ss_ta;
	ss_ctx_P ss_ctx;
	char *which;

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

	/* when to call milter? */
	if (!(cur_status == SM_SUCCESS
	      && SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE)
	      && SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_MAIL)
	      && SSTA_IS_FLAG(ss_ta, SSTA_FL_PM_USE)))
		return SM_SUCCESS;

	which = "sm_s2m_mail";
	ret = sm_s2m_mail(ss_sess, ss_ctx->ssc_s2m_ctx,
			ss_sess->ssse_id, ss_ta->ssta_id, ss_sess->ssse_str, argoffset);
	if (sm_is_success(ret)) {
		SSTA_SET_FLAG(ss_ta, SSTA_FL_PM_CALLED);
		SSSE_SET_FLAG(ss_sess, SSSE_FL_PM_CALLED);
		ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4m2s,
			ss_ctx->ssc_s2m_ctx);
		which = "sm_w4q2s_reply";
	}
	if (sm_is_err(ret)) {
		SSPM_TRY_AGAIN(ss_ctx, ss_sess);
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERROR, 6,
			"sev=ERROR, func=sspm_mail, ss_sess=%s, ss_ta=%s, %s=%m"
			, ss_sess->ssse_id, ss_ta->ssta_id, which, ret);

		/* don't use milter anymore in this session */
		SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE);
		if (SSC_IS_MFLAG(ss_ctx, SSC_MFL_PM_421))
			ret = SMTP_R_SSD;
		else	/* ignore error */
			ret = SM_SUCCESS;
	}
	else if (SMAR_RISQUICK(ret)) {
		if (SMTP_R_ACCEPT == ret)
			SSTA_CLR_FLAG(ss_ta, SSTA_FL_PM_USE);
		/* SSTA_SET_FLAG(ss_ta, SSTA_FL_MAIL_QCK); */
		SMAR_RCLRQUICK(ret);
	}
	else if (SSTA_IS_FLAG(ss_ta, SSTA_FL_DELAY_CHKS)
		 && IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret))
	{
		sm_str_P str;

		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_DEBUG, 12,
			"sev=DBG, func=sspm_mail, ss_sess=%s, ss_ta=%s, %s=%m, reply=%r, text=%@T"
			, ss_sess->ssse_id, ss_ta->ssta_id, which, ret
			, ss_ta->ssta_mail_acc.ssa_reply_code
			, ss_sess->ssse_wr);

		/* delay rejection */
		str = sm_str_dup(NULL, ss_sess->ssse_wr);
		if (str != NULL) {
			(void) sspm_chk_reply(str, ret, SS_PHASE_MAIL);
			ss_ta->ssta_mail_acc.ssa_reply_text = str;
			sm_str_clr(ss_sess->ssse_wr);
			ret = SMTP_R_OK;
		}
		else {
			/* can't accept session: ENOMEM */
			ret = SMTP_R_SSD;
			(void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_MAIL, true);
			sm_log_write(ss_ctx->ssc_lctx,
				SS_LCAT_SERVER, SS_LMOD_SERVER,
				SM_LOG_ERROR, 4,
				"sev=ERROR, func=sspm_mail, ss_sess=%s, ss_ta=%s, sm_str_dup=ENOMEM"
				, ss_sess->ssse_id, ss_ta->ssta_id);
		}
	}
	if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) {
		int temprc;

		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_INFO, 10,
			"sev=INFO, func=sspm_mail, ss_sess=%s, ss_ta=%s, mail=%@N, stat=%r, text=%@T"
			, ss_sess->ssse_id, ss_ta->ssta_id
			, ss_sess->ssse_str
			, ret, ss_sess->ssse_wr);
		if (!SMTP_REPLY_MATCHES_RCODE(ss_sess->ssse_wr, ret, 0, temprc))
			(void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_MAIL, true);
		else
			(void) sspm_chk_reply(ss_sess->ssse_wr, ret, SS_PHASE_MAIL);
	}
	else {
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_INFO, 13,
			"sev=INFO, func=sspm_mail, ss_sess=%s, ss_ta=%s, sm_w4q2s_reply_ar=%m"
			, ss_sess->ssse_id, ss_ta->ssta_id, ret);
		if (SMTP_R_DISCARD == ret) {
			SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD);
			ret = SM_SUCCESS;	/* "normalize" */
		}
		else if (SMTP_R_CONT == ret)
			ret = SM_SUCCESS;	/* "normalize" */
	}
	return ret;
}

/*
**  SSPM_RCPT -- Inform libpmilter about RCPT command
**
**	Parameters:
**		ss_sess -- SMTPS session
**		ss_rcpt -- recipient
**		cur_status -- current status while dealing with RCPT
**
**	Returns:
**		reply code to use for MAIL
*/

sm_ret_T
sspm_rcpt(ss_sess_P ss_sess, ss_rcpt_P ss_rcpt, sm_ret_T cur_status)
{
	sm_ret_T ret;
	ss_ta_P ss_ta;
	ss_ctx_P ss_ctx;
	char *which;

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

	/* when to call milter? */
	if (!((cur_status == SM_SUCCESS ||
	       SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_RCPT_ST))
	      && SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE)
	      && SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_RCPT)
	      && SSTA_IS_FLAG(ss_ta, SSTA_FL_PM_USE)))
		return SM_SUCCESS;

	which = "sm_s2m_rcpt";
	ret = sm_s2m_rcpt(ss_sess, ss_ctx->ssc_s2m_ctx, ss_sess->ssse_id,
			ss_rcpt->ssr_idx, ss_sess->ssse_str, cur_status);
	if (sm_is_success(ret)) {
		SSTA_SET_FLAG(ss_ta, SSTA_FL_PM_CALLED);
		SSSE_SET_FLAG(ss_sess, SSSE_FL_PM_CALLED);
		ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4m2s,
			ss_ctx->ssc_s2m_ctx);
		which = "sm_w4q2s_reply";
	}
	if (sm_is_err(ret)) {
		SSPM_TRY_AGAIN(ss_ctx, ss_sess);
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERROR, 6,
			"sev=ERROR, func=sspm_rcpt, ss_sess=%s, ss_ta=%s, %s=%m"
			, ss_sess->ssse_id, ss_ta->ssta_id, which, ret);

		/* don't use milter anymore in this session */
		SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE);
		if (SSC_IS_MFLAG(ss_ctx, SSC_MFL_PM_421))
			ret = SMTP_R_SSD;
		else	/* ignore error */
			ret = SM_SUCCESS;
	}
	else if (SMAR_RISQUICK(ret)) {
		if (SMTP_R_ACCEPT == ret)
			SSTA_CLR_FLAG(ss_ta, SSTA_FL_PM_USE);
		/* SSTA_SET_FLAG(ss_ta, SSTA_FL_RCPT_QCK); */
		SMAR_RCLRQUICK(ret);

		/* quick:discard -> discard entire transaction */
		if (SMTP_R_DISCARD == ret)
			SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD);
	}
	if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) {
		int temprc;

		if (!SMTP_REPLY_MATCHES_RCODE(ss_sess->ssse_wr, ret, 0, temprc))
			sm_str_clr(ss_sess->ssse_wr);
			/* new error text will be generated later */
		else
			(void) sspm_chk_reply(ss_sess->ssse_wr, ret, SS_PHASE_RCPT);
	}
	else if (SMTP_R_CONT == ret)
		ret = SM_SUCCESS;	/* "normalize" */
	return ret;
}

/*
**  SSPM_DATA -- Inform libpmilter about DATA command
**
**	Parameters:
**		ss_sess -- SMTPS session
**		pskip -- skip msg (input and output)
**
**	Returns:
**		reply code to use for DATA
*/

sm_ret_T
sspm_data(ss_sess_P ss_sess, bool *pskip)
{
	sm_ret_T ret;
	ss_ta_P ss_ta;
	ss_ctx_P ss_ctx;
	char *which;

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

	/* send DATA commant to milter unless DISCARD (skip) is set */
	if (!(!*pskip && SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE)
	    && SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_DATA)
	      && SSTA_IS_FLAG(ss_ta, SSTA_FL_PM_USE)))
		return SM_SUCCESS;

	which = "sm_s2m_data";
	ret = sm_s2m_data(ss_sess, ss_ctx->ssc_s2m_ctx, ss_sess->ssse_id);
	if (sm_is_success(ret)) {
		SSTA_SET_FLAG(ss_ta, SSTA_FL_PM_CALLED);
		SSSE_SET_FLAG(ss_sess, SSSE_FL_PM_CALLED);
		ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4m2s,
			ss_ctx->ssc_s2m_ctx);
		which = "sm_w4q2s_reply";
	}
	if (sm_is_err(ret)) {
		SSPM_TRY_AGAIN(ss_ctx, ss_sess);
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERROR, 6,
			"sev=ERROR, func=sspm_data, ss_sess=%s, ss_ta=%s, %s=%m"
			, ss_sess->ssse_id, ss_ta->ssta_id, which, ret);

		/* don't use milter anymore in this session */
		SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE);
		if (SSC_IS_MFLAG(ss_ctx, SSC_MFL_PM_421))
			ret = SMTP_R_SSD;
		else	/* ignore error */
			ret = SM_SUCCESS;
	}
	else if (SMAR_RISQUICK(ret)) {
		if (SMTP_R_ACCEPT == ret)
			SSTA_CLR_FLAG(ss_ta, SSTA_FL_PM_USE);
		SMAR_RCLRQUICK(ret);
	}
	if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) {
		int temprc;

		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_INFO, 10,
			"sev=INFO, func=sspm_data, ss_sess=%s, ss_ta=%s, stat=%r, text=%@T"
			, ss_sess->ssse_id, ss_ta->ssta_id
			, ret, ss_sess->ssse_wr);
		if (!SMTP_REPLY_MATCHES_RCODE(ss_sess->ssse_wr, ret, 0, temprc))
			(void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_DATA, true);
		else
			(void) sspm_chk_reply(ss_sess->ssse_wr, ret, SS_PHASE_DATA);
	}
	else if (SMTP_R_DISCARD == ret) {
		SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD);
		*pskip = true;
		ret = SM_SUCCESS;	/* "normalize" */
	}
	else if (SMTP_R_CONT == ret)
		ret = SM_SUCCESS;	/* "normalize" */
	return ret;
}

/*
**  SSPM_MSG -- Inform libpmilter about msg chunk
**
**	Parameters:
**		ss_sess -- SMTPS session
**		bufp -- pointer to buffer to transmit
**		bytes2write -- size of buffer
**		isrcvd -- is this the generated Received: header?
**		pskip -- skip msg (input and output)
**
**	Returns:
**		reply code to use for final dot
**
**	Side Effects:
**		on fatal error a reply text is written to ssse_wr
**		on SMTP rejections, ss_ta->ssta_msg_acc is set.
*/

sm_ret_T
sspm_msg(ss_sess_P ss_sess, uchar *bufp, size_t bytes2write, bool isrcvd, bool *pskip)
{
	sm_ret_T ret;
	ss_ta_P ss_ta;
	ss_ctx_P ss_ctx;
	char *which;

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

	/* call milter even if skip is set? */
	if (!(!*pskip
	      && SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE)
	      && ((SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_MSG) && !isrcvd) ||
	          (SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_SND_RCVD) && isrcvd))
	      && SSTA_IS_FLAG(ss_ta, SSTA_FL_PM_USE)
	      && !SSTA_IS_FLAG(ss_ta, SSTA_FL_PM_SKIP_MSG)))
		return SM_SUCCESS;

	sm_log_write(ss_ctx->ssc_lctx,
		SS_LCAT_SERVER, SS_LMOD_SERVER,
		SM_LOG_DEBUG, 15,
		"sev=DBG, func=sspm_msg, ss_sess=%s, ss_ta=%s, bytes2write=%u, bfsize=%d, blksize=%d"
		, ss_sess->ssse_id, ss_ta->ssta_id, (uint) bytes2write
		, (int) f_bfsize(*ss_sess->ssse_fp)
		, (int) ss_sess->ssse_fp->f_blksize
		);

	which = "sm_s2m_msg";
	ret = sm_s2m_msg(ss_sess, ss_ctx->ssc_s2m_ctx, ss_sess->ssse_id,
		bufp, bytes2write,
		SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_MSG_RC)
			? SM_S2M_MSG_REPLY : SM_S2M_MSG_NONE);
	if (sm_is_success(ret)) {
		SSTA_SET_FLAG(ss_ta, SSTA_FL_PM_CALLED);
		SSSE_SET_FLAG(ss_sess, SSSE_FL_PM_CALLED);
		if (SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_MSG_RC)) {
			ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4m2s,
				ss_ctx->ssc_s2m_ctx);
			which = "sm_w4q2s_reply";
		}
	}
	if (sm_is_err(ret)) {
		SSPM_TRY_AGAIN(ss_ctx, ss_sess);
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERROR, 7,
			"sev=ERROR, func=sspm_msg, ss_sess=%s, ss_ta=%s, %s=%m, bytes2write=%d"
			, ss_sess->ssse_id, ss_ta->ssta_id, which, ret
			, (int) bytes2write);

		/* don't use milter anymore in this session */
		SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE);
		if (SSC_IS_MFLAG(ss_ctx, SSC_MFL_PM_421)) {
			ret = SMTP_R_SSD;
			sm_str_scopy(ss_sess->ssse_wr,
				"421 4.3.0 Cannot contact milter.\r\n");
			*pskip = true;
		}
		else	/* ignore error */
			ret = SM_SUCCESS;
	}
	else if (SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_MSG_RC)) {
		if (SMAR_RISQUICK(ret)) {
			if (SMTP_R_ACCEPT == ret)
				SSTA_CLR_FLAG(ss_ta, SSTA_FL_PM_USE);
			SMAR_RCLRQUICK(ret);
		}
		if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) {
			int temprc;
			sm_str_P str;

			str = NULL;
			sm_log_write(ss_ctx->ssc_lctx,
				SS_LCAT_SERVER, SS_LMOD_SERVER,
				SM_LOG_INFO, 10,
				"sev=INFO, func=sspm_msg, ss_sess=%s, ss_ta=%s, stat=%r, text=%@T"
				, ss_sess->ssse_id, ss_ta->ssta_id
				, ret, ss_sess->ssse_wr);
			if (!SMTP_REPLY_MATCHES_RCODE(ss_sess->ssse_wr, ret, 0, temprc))
				(void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_DOT, true);
			else
				(void) sspm_chk_reply(ss_sess->ssse_wr, ret, SS_PHASE_DOT);

			ss_ta->ssta_msg_acc.ssa_map_result = SM_ACC_FOUND;
			ss_ta->ssta_msg_acc.ssa_reply_code = ret;
			str = sm_str_dup(NULL, ss_sess->ssse_wr);
			if (str != NULL) {
				ss_ta->ssta_msg_acc.ssa_reply_text = str;
				sm_str_clr(ss_sess->ssse_wr);
			}
			SSTA_CLR_FLAG(ss_ta, SSTA_FL_PM_USE);
		}
		else if (SMTP_R_DISCARD == ret) {
			SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD);
			*pskip = true;
			ret = SM_SUCCESS; /* "normalize" */
		}
		else if (SMTP_R_SKIP == ret) {
			SSTA_SET_FLAG(ss_ta, SSTA_FL_PM_SKIP_MSG);
			ret = SM_SUCCESS; /* "normalize" */
		}
		else if (SMTP_R_CONT == ret)
			ret = SM_SUCCESS;	/* "normalize" */
	}
	return ret;
}

/*
**  SSPM_EOB -- Inform libpmilter about last msg chunk
**
**	Parameters:
**		ss_sess -- SMTPS session
**		pskip -- skip msg (input and output)
**
**	Returns:
**		reply code to use for dot
*/

sm_ret_T
sspm_eob(ss_sess_P ss_sess, uchar *bufp, size_t bytes2write, bool *pskip)
{
	sm_ret_T ret;
	ss_ta_P ss_ta;
	ss_ctx_P ss_ctx;
	char *which;

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

	/* NOTE: pmilter isn't called for any of the errors above! */
	if (!(SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE)
	      && SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_MSG)
	      && SSTA_IS_FLAG(ss_ta, SSTA_FL_PM_USE)))
		return SM_SUCCESS;

	which = "sm_s2m_msg";
	ret = sm_s2m_msg(ss_sess, ss_ctx->ssc_s2m_ctx,
		ss_sess->ssse_id, bufp, bytes2write, SM_S2M_MSG_LAST|SM_S2M_MSG_REPLY);
	if (sm_is_success(ret)) {
		SSTA_SET_FLAG(ss_ta, SSTA_FL_PM_CALLED);
		SSSE_SET_FLAG(ss_sess, SSSE_FL_PM_CALLED);
		ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4m2s,
			ss_ctx->ssc_s2m_ctx);
		which = "sm_w4q2s_reply";
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_INFO, 19,
			"sev=INFO, func=sspm_eob, ss_sess=%s, ss_ta=%s, stat=%m, text=%@T"
			, ss_sess->ssse_id, ss_ta->ssta_id
			, ret, ss_sess->ssse_wr);
	}
	if (sm_is_err(ret)) {
		SSPM_TRY_AGAIN(ss_ctx, ss_sess);
		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_ERROR, 6,
			"sev=ERROR, func=sspm_eob, ss_sess=%s, ss_ta=%s, %s=%m"
			, ss_sess->ssse_id, ss_ta->ssta_id, which, ret);

		/* don't use milter anymore in this session */
		SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE);
		if (SSC_IS_MFLAG(ss_ctx, SSC_MFL_PM_421))
			ret = SMTP_R_SSD;
		else	/* ignore error */
			ret = SM_SUCCESS;
	}
	else if (SMAR_RISQUICK(ret)) {
		if (SMTP_R_ACCEPT == ret)
			SSTA_CLR_FLAG(ss_ta, SSTA_FL_PM_USE);
		SMAR_RCLRQUICK(ret);
	}
	if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) {
		int temprc;

		sm_log_write(ss_ctx->ssc_lctx,
			SS_LCAT_SERVER, SS_LMOD_SERVER,
			SM_LOG_INFO, 10,
			"sev=INFO, func=sspm_eob, ss_sess=%s, ss_ta=%s, stat=%r, text=%@T"
			, ss_sess->ssse_id, ss_ta->ssta_id
			, ret, ss_sess->ssse_wr);
		if (!SMTP_REPLY_MATCHES_RCODE(ss_sess->ssse_wr, ret, 0, temprc))
			(void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_DOT, true);
		else
			(void) sspm_chk_reply(ss_sess->ssse_wr, ret, SS_PHASE_DOT);
	}
	else if (SMTP_R_DISCARD == ret) {
		SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD);
		*pskip = true;
		ret = SM_SUCCESS; /* "normalize" */
	}
	else if (SMTP_R_CONT == ret)
		ret = SM_SUCCESS;	/* "normalize" */
	if (SMTP_R_RPLCMSG == ret)
		ret = ss_rplcmsg(ss_sess);
	return ret;
}
#endif /* MTA_USE_PMILTER */


syntax highlighted by Code2HTML, v. 0.9.1