/*
 * 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.
 */

#include "sm/generic.h"
SM_RCSID("@(#)$Id: smtps2pmilter.c,v 1.35 2007/02/03 03:23:04 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/io.h"
#include "sm/rcb.h"
#include "sm/reccom.h"
#include "pmilter.h"
#include "sm/pmilter.h"
#include "sm/pmfdef.h"
#include "sm/pmfapi.h"

#define PMSS_RC_MINSZ	28
#define PMSS_MAX_REC_LEN (65 * 1024)

#if 0

generic reply code:

		/* prepare reply (async) */
		ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
		if (sm_is_err(ret))
			goto errPART;
		inwaitq = true;

		/* call milter function (rcbe required for response) */
		ret2 = sm_pmilt_PART(pmss_ctx, pmse_ctx, rcbe);
		if (sm_is_err(ret2))
			goto errPART;

		/* put reply into write queue */
		ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
		if (sm_is_err(ret))
			goto errPART;

#endif /* 0 */

/*
**  SM_PMILT_SMTPSFIND -- Find SMTPS id
**
**	Parameters:
**		pmg_ctx -- pmilter (library) context
**		pmss_ctx -- task context pmilter/SMTP server
**
**	Returns:
**		>=0: index
**		<0: usual sm_error code
**
**	Last code review:
*/

static sm_ret_T
sm_pmilt_smtpsfind(pmg_ctx_P pmg_ctx, pmss_ctx_P pmss_ctx)
{
	int r;
	uint u;
	uint32_t j;
	sm_ret_T ret;
	pmss_ctx_P pmss2_ctx;

	r = pthread_mutex_lock(&pmg_ctx->pmg_mutex);
	SM_LOCK_OK(r);
	if (r != 0) {
		/* LOG */
		PM_LEV_DPRINTF(0, (PM_DEBFP, "sev=ERROR, func=sm_pmilt_smtpsfind, lock=%d\n", r));
		return sm_error_temp(SM_EM_PMILTER, r);
	}

	ret = sm_error_perm(SM_EM_PMILTER, SM_E_NOTFOUND);

	/* search for sss ctx */
	for (j = 1, u = 0; u < SM_ARRAY_SIZE(pmg_ctx->pmg_sctx); u++, j *= 2) {
		if ((pmg_ctx->pmg_sused & j) != 0) {
			pmss2_ctx = pmg_ctx->pmg_sctx[u];
			if (pmss2_ctx != pmss_ctx &&
			    pmss2_ctx->pmss_id == pmss_ctx->pmss_id)
			{
				ret = u;
				break;
			}
		}
	}
	r = pthread_mutex_unlock(&pmg_ctx->pmg_mutex);
	SM_ASSERT(r == 0);
	if (r != 0 && sm_is_success(ret))
		ret = sm_error_perm(SM_EM_PMILTER, r);
	return ret;
}

/*
**  SM_PMILT_SENDMACLIST -- Send list of macros to SMTPS
**
**	Parameters:
**		pmss_ctx -- task context pmilter/SMTP server
**		rcb -- RCB to send to SMTPS
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
sm_pmilt_sendmaclist(pmss_ctx_P pmss_ctx, sm_rcb_P rcb)
{
	sm_ret_T ret;
	uint i, j;
	uint32_t macm;
	bool first;

	ret = SM_SUCCESS;
	for (i = 0; !sm_is_err(ret) && i < PM_SMST_MAX; i++) {
		first = true;
		for (j = 0;
		     !sm_is_err(ret) && j < PM_MAX_MACROS &&
		     (macm = pmss_ctx->pmss_mac_names[i][j]) != PMM_END;
		     j++)
		{
#if PM_SE_TA_ID_LOCAL
			if (PMM_SEID == macm || PMM_MAIL_TAID == macm)
				continue;
#endif
			if (first) {
				ret = sm_rcb_put3uint32(rcb, 4, RT_M2S_MACW, i);
				first = false;
				if (sm_is_err(ret))
					break;
			}
			ret = sm_rcb_put3uint32(rcb, 4, RT_M2S_MACM, macm);
		}
	}
	return ret;
}

/*
**  SM_PMSS_REACT -- Decode data received from SMTPS
**
**	Parameters:
**		pmss_ctx -- task context pmilter/SMTP server
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
**
**	Locking:
**		XXX who locks pmss_ctx?
**
**	Called by: sm_smtps2pmilt
*/

static sm_ret_T
sm_pmss_react(pmss_ctx_P pmss_ctx, sm_evthr_task_P tsk)
{
	uint32_t v, l, rt, tl;
	sm_ret_T ret, ret2, rv;
	bool inwaitq;
	sm_rcb_P rcb;
	sm_rcbe_P rcbe;
	pmg_ctx_P pmg_ctx;
	pmse_ctx_P pmse_ctx;
	sessta_id_T se_id;

	SM_IS_PMSS_CTX(pmss_ctx);
	ret = rv = SM_SUCCESS;
	inwaitq = false;
	rcbe = NULL;
	pmse_ctx = NULL;

	/*
	**  XXX Todo: add error codes before each goto err*
	**	this is necessary to figure out what error happened
	**	(at least for debugging...)
	**	it is also necessary in some cases to send a reply
	**	back to SMTPS, e.g., if PMILTER can't allocate memory
	**	then SMTPS must be told about it...
	**
	**  In general PMILTER doesn't reply to data it can't decode.
	**  We assume that the sender is fubared and a reply won't help.
	**  Let it figure out itself that something went wrong.
	**  However, we should log an error (logging is completely missing).
	*/

	/* Decode rcb */
	rcb = pmss_ctx->pmss_com.rcbcom_rdrcb;
	ret = sm_rcb_open_dec(rcb);
	if (sm_is_err(ret)) {
		rv = ret;
		goto error;
	}
	pmg_ctx = pmss_ctx->pmss_pmg_ctx;
	SM_IS_PMG_CTX(pmg_ctx);

	/* Total length of record */
	ret = sm_rcb_getuint32(rcb, &tl);
	if (sm_is_err(ret) || tl > PMSS_MAX_REC_LEN || tl > sm_rcb_getlen(rcb)) {
		rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_RCB2LONG);
		goto err2;
	}

	/* Decode data, act accordingly... */

	/* Protocol header: version */
	ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
	if (sm_is_err(ret)) {
		rv = ret;
		goto err2;
	}
	if (l != 4 || rt != RT_PROT_VER || v != PROT_VER_RT) {
		rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_V_MISM);
		goto err2;
	}

	/* SMTPS (new/existing/close) ID */
	ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
PM_LEV_DPRINTF(3, (PM_DEBFP, "sev=DBG, rt=%X, l=%d, smtpsid=%d\n", rt, l, v));
	if (sm_is_err(ret) || l != 4) {
		rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
		goto err2;
	}
	if (RT_S2M_NID == rt) {
		pmilter_P pmilter;

		/* New SMTPS */
		if (pmss_ctx->pmss_status != PMSS_ST_NONE ||
		    pmss_ctx->pmss_id != PMSS_ID_NONE)
		{
			/* XXX Internal error? Stop task? SMTPS id exists */
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			/* XXX use different error! */
			goto err2;
		}
		pmss_ctx->pmss_id = v;
		pmss_ctx->pmss_cur_session = 0;
		ret = sm_pmilt_smtpsfind(pmg_ctx, pmss_ctx);
		if (ret >= 0) {
PM_LEV_DPRINTF(0, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, nid=%d, exists=%d\n", pmss_ctx->pmss_id, ret));
			/* XXX Internal error? Stop task? SMTPS id exists */
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			/* XXX use different error! */
			goto err2;
		}

		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_S2M_PROT) {
			rv = sm_is_err(ret) ? ret
				: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto err2;
		}
		/* XXX check protocol version */

		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_S2M_MAXTHRDS) {
			rv = sm_is_err(ret) ? ret
				: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto err2;
		}
		pmss_ctx->pmss_max_thrs = v;

		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_S2M_CAP) {
			rv = sm_is_err(ret) ? ret
				: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto err2;
		}
		pmss_ctx->pmss_cap = v & SM_SCAP_PM_ALL;
#if 0
		if ((v & SM_SCAP_PM_ALL) != v)
			sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
				QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
				SM_LOG_INFO, 4,
				"sev=INFO, func=sm_pmss_react, server_cap=%#x, libpmilter_cap=%#X"
				, v, SM_SCAP_PM_ALL);
#endif

		while (!SM_RCB_ISEOB(rcb)) {
			ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
			if (sm_is_err(ret) || l != 4) {
				rv = sm_is_err(ret) ? ret
					: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
				goto err2;
			}
			switch (rt) {
			  case RT_S2M_FCT:
				pmss_ctx->pmss_fct = v & SM_SFCT_PM_ALL;
#if 0
				if ((v & SM_SFEAT_PM_ALL) != v)
					sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
						QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
						SM_LOG_INFO, 4,
						"sev=INFO, func=sm_pmss_react, server_fct=%#x, libpmilter_fct=%#X"
						, v, SM_SFCT_PM_ALL);
#endif
				break;

			  case RT_S2M_FEAT:
				pmss_ctx->pmss_feat = v & SM_SFEAT_PM_ALL;
#if 0
				if ((v & SM_SFEAT_PM_ALL) != v)
					sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
						QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
						SM_LOG_INFO, 4,
						"sev=INFO, func=sm_pmss_react, server_feat=%#x, libpmilter_feat=%#X"
						, v, SM_SFEAT_PM_ALL);
#endif
				break;

			  case RT_S2M_MISC:
				pmss_ctx->pmss_misc = v & SM_SMISC_PM_ALL;
#if 0
				if ((v & SM_SMISC_PM_ALL) != v)
					sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
						QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
						SM_LOG_INFO, 4,
						"sev=INFO, func=sm_pmss_react, server_misc=%#x, libpmilter_misc=%#X"
						, v, SM_SMISC_PM_ALL);
#endif
				break;


			  default:
				ret = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
				goto err2;
			}
		}

		pmss_ctx->pmss_status = PMSS_ST_START;

		SM_ASSERT(pmss_ctx->pmss_pmg_ctx != NULL);
		pmilter = pmss_ctx->pmss_pmg_ctx->pmg_pmilter;
		SM_ASSERT(pmilter != NULL);
		pmss_ctx->pmss_pmcap = pmilter->pmfi_dfl_cap;
		pmss_ctx->pmss_pmfct = pmilter->pmfi_dfl_fct;
		pmss_ctx->pmss_pmfeat = pmilter->pmfi_dfl_feat;
		pmss_ctx->pmss_pmmisc = pmilter->pmfi_dfl_misc;
		if (pmilter->pmfi_negotiate != NULL) {
			/* check protocol, capabilities, requirements, etc */
			ret = (*pmilter->pmfi_negotiate)(pmss_ctx,
					pmss_ctx->pmss_cap,
					pmss_ctx->pmss_fct,
					pmss_ctx->pmss_feat,
					pmss_ctx->pmss_misc,
					&pmss_ctx->pmss_pmcap,
					&pmss_ctx->pmss_pmfct,
					&pmss_ctx->pmss_pmfeat,
					&pmss_ctx->pmss_pmmisc);
#if 0
			if (sm_is_err(ret))
				/* XXX */ ;
#endif
		}

		/*
		**  Send a reply to let client know that the connection
		**  has been accepted.
		*/

		ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
		if (sm_is_err(ret))
			goto err2;
		inwaitq = true;

		/* this doesn't need to be async ... */
		ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST,
			SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
			SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id,
			SM_RCBV_INT, RT_M2S_PROT, SM_PMILTER_PROT,
			SM_RCBV_INT, RT_M2S_CAP, pmss_ctx->pmss_pmcap,
			SM_RCBV_INT, RT_M2S_FCT, pmss_ctx->pmss_pmfct,
			SM_RCBV_INT, RT_M2S_FEAT, pmss_ctx->pmss_pmfeat,
			SM_RCBV_END);
		if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nid=%d, sm_pcb_putv=%x\n", pmss_ctx->pmss_id, v, ret));
			goto err2;
		}
		ret = sm_pmilt_sendmaclist(pmss_ctx, &rcbe->rcbe_rcb);
		if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nid=%d, pm_sendmaclist=%r\n", pmss_ctx->pmss_id, v, ret));
			goto err2;
		}

		ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
		if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nid=%d, sm_rcbcom_endrep=%x\n", pmss_ctx->pmss_id, v, ret));
			goto err2;
		}
PM_LEV_DPRINTF(5, (PM_DEBFP, "sev=DBG, nid=ok\n"));

		goto done;
	}
	else if (RT_S2M_ID == rt) {
		/* SMTPS id just for identification */
		if (PMSS_ST_NONE == pmss_ctx->pmss_status || pmss_ctx->pmss_id != v) {
			/* XXX Internal error? Stop task? SMTPS id exists */
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			/* XXX use different error! */
			goto err2;
		}
	}
	else if (RT_S2M_CID == rt) {
		/* SMTPS shuts down */
		if (PMSS_ST_NONE == pmss_ctx->pmss_status || pmss_ctx->pmss_id != v) {
			/* XXX Internal error? Stop task? SMTPS id exists */
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			/* XXX use different error! */
			goto err2;
		}
		/* check for EOB? not really: we shut down anyway */

		pmss_ctx->pmss_status = PMSS_ST_SH_DOWN;
		(void) sm_rcb_close_dec(pmss_ctx->pmss_com.rcbcom_rdrcb);

		/*
		**  We assume that all no open sessions/transactions exist,
		**  i.e., SMTPS properly terminated them before sending this
		**  message.
		*/

		/* Terminate (delete) this task, pmss_ctx is cleaned in caller */
		return EVTHR_DEL;
	}
	else
		goto err2;

	/* RT_S2M_ID == rt is the only case in which we continue here */

	/* what's next? */
	ret = sm_rcb_get2uint32(rcb, &l, &rt);
	if (sm_is_err(ret)) {
		rv = ret;
		goto err2;
	}
	/* XXX always session id? */

	if (l != SMTP_STID_SIZE ||
	    (rt != RT_S2M_NSEID && rt != RT_S2M_CSEID && rt != RT_S2M_SEID))
	{
		rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
		goto err2;
	}

	/* session id */
	ret = sm_rcb_getn(rcb, (uchar *) se_id, l);
	if (sm_is_err(ret)) {
		rv = ret;
		goto errcseid;
	}

	if (RT_S2M_CSEID == rt || RT_S2M_SEID == rt) {
		/* lookup session id to find session context */
		ret = sm_pmse_find(pmss_ctx, se_id, &pmse_ctx);
		if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, sess-id=%s, sm_pmse_find=not_found\n", pmss_ctx->pmss_id, se_id));
			goto errhelo;
		}
		pmse_ctx->pmse_cmd_status = 0;
	}

	if (RT_S2M_SEID == rt) {
		/* get next record */
		ret = sm_rcb_get2uint32(rcb, &l, &rt);
		if (sm_is_err(ret)) {
			rv = ret;
			goto err2;
		}
	}

	if (RT_S2M_NSEID == rt) {
		ret = sm_pmse_new(pmg_ctx, pmss_ctx, &pmse_ctx);
		if (sm_is_err(ret))
			goto err2;
		strlcpy(pmse_ctx->pmse_se_id, se_id, sizeof(pmse_ctx->pmse_se_id));
		ret = sm_getsmtpsid(pmse_ctx->pmse_se_id);
		if (ret != pmss_ctx->pmss_id) {
			/* XXX SMTPS id doesn't match */
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			/* XXX use different error! */
			goto errnseid;
		}

		/* XXX client IP address; must match structure later on! */
		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_S2M_IPV4) {
			rv = sm_is_err(ret) ? ret
				: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errnseid;
		}
		pmse_ctx->pmse_cltipv4 = v;
		++pmss_ctx->pmss_cur_session;

PM_LEV_DPRINTF(3, (PM_DEBFP, "func=sm_pmss_react, id=%d, nsess-id=%s, IP=%A\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id, v));

		/* port */
		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_S2M_PORT) {
			rv = sm_is_err(ret) ? ret
				: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errnseid;
		}
		/* populate SOCKADDR for call to nseid() */

		ret = sm_rcb_get2uint32(rcb, &l, &rt);
		if (sm_is_err(ret) || rt != RT_S2M_HOST) {
			rv = sm_is_err(ret) ? ret
				: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errnseid;
		}
		SM_STR_FREE(pmse_ctx->pmse_arg1);
		ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg1, l);
		if (sm_is_err(ret))
			goto errnseid;
		(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_CONNECT);
		ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_CONNECT, rcb);
		if (sm_is_err(ret))
			goto errnseid;

		ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
		if (sm_is_err(ret))
			goto errnseid;
		inwaitq = true;

		ret = sm_pmilt_nseid(pmss_ctx, pmse_ctx, rcbe);
		SM_STR_FREE(pmse_ctx->pmse_arg1);
		if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nsess-id=%s, sm_pmilt_nseid=%x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id, ret));
			goto errnseid2;
		}
		ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, evthr_en_wr=%p, done=%x\n", &pmss_ctx->pmss_com, ret));
		if (sm_is_err(ret)) {
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nsess-id=%s, sm_rcbcom_endrep=%x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id, ret));
			goto errnseid2;
		}
		return PMILT_R_ASYNC;

  errnseid2:
		/* XXX this needs to undo all operations from sm_pmilt_nseid() */
  errnseid:
		if (pmse_ctx != NULL) {
			(void) sm_pmse_free(pmss_ctx, pmse_ctx);
			pmse_ctx = NULL;
		}
#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_ERR, 4,
			"sev=ERROR, func=sm_pmss_react, sess_new=%d", ret);
#endif /* 0 */
		goto err2;
	}
	else if (RT_S2M_CSEID == rt) {
		ret = sm_getsmtpsid(se_id);
		if (ret != pmss_ctx->pmss_id) {
			/* XXX SMTPS id doesn't match */
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			/* XXX use different error! */
			goto errcseid;
		}

PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, csess-id=%s\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id));
		/* check for EOR? */

		/* call milter function */
		ret2 = sm_pmilt_cseid(pmss_ctx, pmse_ctx);
		if (sm_is_err(ret2))
			goto errcseid;

#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_INFO, 14,
			"func=sm_pmss_react, id=%d, close_sess=%s",
			pmss_ctx->pmss_id, pmse_ctx->pmse_se_id);
#endif /* 0 */
		sm_pmse_free(pmss_ctx, pmse_ctx);
		--pmss_ctx->pmss_cur_session;

		/* XXX no reply??? */
		goto done;

  errcseid:
		/* more cleanup? (pmse_ctx isn't allocated, don't free it!) */
#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_ERR, 4,
			"sev=ERROR, func=sm_pmss_react, sess_close=%d", ret);
#endif /* 0 */
		goto err2;
	}
	else if (RT_S2M_HELO == rt || RT_S2M_EHLO == rt) {
		bool ehlo;

		ehlo = RT_S2M_EHLO == rt;
		ret = sm_getsmtpsid(se_id);
		if (ret != pmss_ctx->pmss_id) {
			/* XXX SMTPS id doesn't match */
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			/* XXX use different error! */
			goto errhelo;
		}

		SM_STR_FREE(pmse_ctx->pmse_arg1);
		ret = sm_rcb_getn0str(rcb, &pmse_ctx->pmse_arg1, l);
		if (sm_is_err(ret))
			goto errhelo;
		(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_EHLO);
		ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_EHLO, rcb);
		if (sm_is_err(ret))
			goto errnseid;

		ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
		if (sm_is_err(ret))
			goto errhelo;
		inwaitq = true;

		ret2 = sm_pmilt_helo(pmss_ctx, pmse_ctx, rcbe, ehlo);
		SM_STR_FREE(pmse_ctx->pmse_arg1);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_helo=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));

		/* check for EOR? */
		if (sm_is_err(ret2))
			goto errhelo;

		ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
		if (sm_is_err(ret))
			goto errhelo;

		return PMILT_R_ASYNC;

  errhelo:
		goto err2;
	}
	else if (RT_S2M_NTAID == rt) {
		if (l != SMTP_STID_SIZE) {
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto err2;
		}

		/* transaction id */

		ret = sm_rcb_getn(rcb, (uchar *) pmse_ctx->pmse_ta_id, l);
		if (sm_is_err(ret)) {
			rv = ret;
			goto errntaid;
		}
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id));
		ret = sm_getsmtpsid(pmse_ctx->pmse_ta_id);
		if (ret != pmss_ctx->pmss_id) {
			/* XXX SMTPS id doesn't match */
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			/* XXX use different error! */
			goto errntaid;
		}

		ret = sm_rcb_get2uint32(rcb, &l, &rt);
		if (sm_is_err(ret) || rt != RT_S2M_MAIL || l >= tl) {
			rv = sm_is_err(ret) ? ret
				: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errntaid;
		}
		SM_STR_FREE(pmse_ctx->pmse_arg1);
		ret = sm_rcb_getn0str(rcb, &pmse_ctx->pmse_arg1, l);
		if (sm_is_err(ret))
			goto errntaid;
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, mail=%S, l=%d\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_arg1, l));

		(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_MAIL);
		while (!SM_RCB_ISEOB(rcb)) {
			ret = sm_rcb_get2uint32(rcb, &l, &rt);
			if (sm_is_err(ret))
				goto errntaid;
			switch (rt) {
			  case RT_S2M_ARG:
				if (l >= tl) {
					rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
					goto errntaid;
				}
				SM_STR_FREE(pmse_ctx->pmse_arg2);
				ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg2, l);
				if (sm_is_err(ret))
					goto errntaid;
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, mail=%S, args=%S\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_arg1, pmse_ctx->pmse_arg2));
				break;
			  case RT_S2M_MACM:
				ret = sm_pmilt_rdmacro(pmse_ctx, PM_SMST_MAIL, rcb);
				if (sm_is_err(ret))
					goto errntaid;
				break;
			  default:
				/* skip over other data */
				ret = sm_rcb_skip(rcb, l);
				if (sm_is_err(ret))
					goto errntaid;
				break;
			}
		}

		ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
		if (sm_is_err(ret))
			goto errntaid2;
		inwaitq = true;

		ret2 = sm_pmilt_mail(pmss_ctx, pmse_ctx, rcbe);
		SM_STR_FREE(pmse_ctx->pmse_arg1);
		SM_STR_FREE(pmse_ctx->pmse_arg2);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_mail=%r\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));

		if (sm_is_err(ret2))
			goto errntaid;

		ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
		if (sm_is_err(ret))
			goto errntaid2;
#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_INFO, 10,
			"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
			pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
			pmse_ctx->pmse_mail_pa);
#endif /* 0 */

		return PMILT_R_ASYNC;

  errntaid2:
  errntaid:
#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_ERR, 4,
			"sev=ERROR, func=sm_pmss_react, ta_new=%d", ret);
#endif /* 0 */
		SM_STR_FREE(pmse_ctx->pmse_arg1);
		SM_STR_FREE(pmse_ctx->pmse_arg2);
		goto err2;
	}
	else if (RT_S2M_RCPT_IDX == rt) {
		SM_STR_FREE(pmse_ctx->pmse_arg1);
		if (l != 4) {
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errrcpt;
		}
		ret = sm_rcb_getuint32(rcb, &v);
		if (sm_is_err(ret)) {
			rv = ret;
			goto errrcpt;
		}
		pmse_ctx->pmse_rcpt_idx = v;
		ret = sm_rcb_get2uint32(rcb, &l, &rt);
		if (sm_is_err(ret) || rt != RT_S2M_RCPT || l > tl) {
			rv = sm_is_err(ret) ? ret
				: sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errrcpt;
		}

		ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg1, l);
		if (sm_is_err(ret))
			goto errrcpt;

		(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_RCPT);
		while (!SM_RCB_ISEOB(rcb)) {
			ret = sm_rcb_get2uint32(rcb, &l, &rt);
			if (sm_is_err(ret))
				goto errrcpt;
			switch (rt) {
			  case RT_S2M_ARG:
				if (l >= tl) {
					rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
					goto errrcpt;
				}
				SM_STR_FREE(pmse_ctx->pmse_arg2);
				ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg2, l);
				if (sm_is_err(ret))
					goto errrcpt;
				break;
			  case RT_S2M_MACM:
				ret = sm_pmilt_rdmacro(pmse_ctx, PM_SMST_RCPT, rcb);
				if (sm_is_err(ret))
					goto errrcpt;
				break;
			  case RT_S2M_RCPT_ST:
				if (l != 4) {
					rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
					goto errrcpt;
				}
				ret = sm_rcb_getuint32(rcb, &v);
				if (sm_is_err(ret)) {
					rv = ret;
					goto errrcpt;
				}
				pmse_ctx->pmse_cmd_status = v;
				break;
			  default:
				/* skip over other data */
				ret = sm_rcb_skip(rcb, l);
				if (sm_is_err(ret))
					goto errrcpt;
				break;
			}
		}

		ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
		if (sm_is_err(ret))
			goto errrcpt2;
		inwaitq = true;

		ret2 = sm_pmilt_rcpt(pmss_ctx, pmse_ctx, rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_rcpt=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));
		SM_STR_FREE(pmse_ctx->pmse_arg1);
		SM_STR_FREE(pmse_ctx->pmse_arg2);
		pmse_ctx->pmse_cmd_status = 0;

		if (sm_is_err(ret2))
			goto errrcpt2;

		ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
		if (sm_is_err(ret))
			goto errrcpt2;
#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_INFO, 10,
			"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
			pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
			pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */

		return PMILT_R_ASYNC;

  errrcpt2:
		/*
		**  More cleanup? See below.
		*/

		/* fall through ... */

  errrcpt:
		SM_STR_FREE(pmse_ctx->pmse_arg1);
		SM_STR_FREE(pmse_ctx->pmse_arg2);
#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_ERR, 4,
			"sev=ERROR, func=sm_pmss_react, in_ss_ta=%x, rv=%x"
			, ret, rv);
#endif /* 0 */
		goto err2;
	}
	else if (RT_S2M_DATA == rt) {
		if (l != 4) {
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errdata;
		}
		ret = sm_rcb_getuint32(rcb, &v);
		if (sm_is_err(ret)) {
			rv = ret;
			goto errdata;
		}

		(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_DATA);
		ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_DATA, rcb);
		if (sm_is_err(ret))
			goto errdata;

		ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
		if (sm_is_err(ret))
			goto errdata2;
		inwaitq = true;

		ret2 = sm_pmilt_data(pmss_ctx, pmse_ctx, rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_data=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));

		if (sm_is_err(ret2))
			goto errdata2;

		ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
		if (sm_is_err(ret))
			goto errdata2;
#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_INFO, 10,
			"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
			pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
			pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */

		return PMILT_R_ASYNC;

  errdata2:
		/*
		**  More cleanup? See below.
		*/

		/* fall through ... */

  errdata:
		goto err2;
	}
	else if (RT_S2M_MSG == rt) {
		uchar *msg;

		msg = NULL;
		if (l >= tl) {
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errmsg;
		}

		msg = (uchar *) sm_malloc(l);
		if (NULL == msg) {
			rv = sm_error_temp(SM_EM_PMILTER, ENOMEM);
			goto errmsg;
		}
		ret = sm_rcb_getn(rcb, msg, l);
		if (sm_is_err(ret)) {
			rv = ret;
			goto errmsg;
		}

		/*
		**  usually no reply ... however: how to make this "async",
		**  i.e., put task back into queue?
		*/

		if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) {
			ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
			if (sm_is_err(ret))
				goto errmsg2;
			inwaitq = true;
		}

		ret2 = sm_pmilt_msg(pmss_ctx, pmse_ctx, msg, l, rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_msg=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));

		/* check for EOR? */
		if (sm_is_err(ret2))
			goto errmsg2;

		if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) {
			ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
			if (sm_is_err(ret))
				goto errmsg2;
		}

#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_INFO, 10,
			"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
			pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
			pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */

		SM_FREE(msg);
		if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC))
			return PMILT_R_ASYNC;
		goto done;

  errmsg2:
		/*
		**  More cleanup? See below.
		*/

		/* fall through ... */

  errmsg:
		SM_FREE(msg);
		goto err2;
	}
	else if (RT_S2M_DOT == rt) {
		uchar *msg;

		msg = NULL;
		if (l >= tl) {
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errdot;
		}

		msg = (uchar *) sm_malloc(l);
		if (NULL == msg) {
			rv = sm_error_temp(SM_EM_PMILTER, ENOMEM);
			goto errdot;
		}
		ret = sm_rcb_getn(rcb, msg, l);
		if (sm_is_err(ret)) {
			rv = ret;
			goto errdot;
		}

		(void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_DOT);
		ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_DOT, rcb);
		if (sm_is_err(ret))
			goto errdot;

		ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe);
		if (sm_is_err(ret))
			goto errdot2;
		inwaitq = true;

		ret2 = sm_pmilt_dot(pmss_ctx, pmse_ctx, msg, l, rcbe);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_dot=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));

		/* check for EOR? */
		if (sm_is_err(ret2))
			goto errdot2;

		ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe);
		if (sm_is_err(ret))
			goto errdot2;
#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_INFO, 10,
			"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
			pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
			pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */

		SM_FREE(msg);
		return PMILT_R_ASYNC;

  errdot2:
		/*
		**  More cleanup? See below.
		*/

		/* fall through ... */

  errdot:
		SM_FREE(msg);
		goto err2;
	}
	else if (RT_S2M_ABORT_TA == rt) {
		if (l != 4) {
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto errabortta;
		}
		ret = sm_rcb_getuint32(rcb, &v);
		if (sm_is_err(ret)) {
			rv = ret;
			goto errabortta;
		}

		ret2 = sm_pmilt_abort_ta(pmss_ctx, pmse_ctx);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_abort_ta=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2));

		/* check for EOR? */
		if (sm_is_err(ret2))
			goto errabortta2;

#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_INFO, 10,
			"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
			pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
			pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */

		goto done;

  errabortta2:
		/*
		**  More cleanup? See below.
		*/

		/* fall through ... */

  errabortta:
		goto err2;
	}
	else if (RT_S2M_MSG_RPLC_STAT == rt) {
		if (l != 4) {
			rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR);
			goto err2;
		}
		ret = sm_rcb_getuint32(rcb, &v);
		if (sm_is_err(ret)) {
			rv = ret;
			goto err2;
		}

		ret2 = sm_pmilt_msg_rplc_stat(pmss_ctx, pmse_ctx, v);
PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, ta-id=%s, status=%#x, sm_sm_pmilt_msg_rplc_stat=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, v, ret2));

		/* check for EOR? */
		if (sm_is_err(ret2))
			goto err2;

#if 0
		sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx,
			QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS,
			SM_LOG_INFO, 10,
			"func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S",
			pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id,
			pmse_ctx->pmse_rcpt_pa);
#endif /* 0 */

		goto done;
	}
#if 0
	else if (RT_S2M_CTAID == rt) {
	}
#endif /* 0 */
	else
		goto err2;

  done:
	if (!inwaitq) {
		ret = sm_rcb_close_dec(pmss_ctx->pmss_com.rcbcom_rdrcb);
		(void) sm_rcb_open_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb);
	}
	if (SM_SUCCESS == ret && inwaitq)
		return PMILT_R_ASYNC;
	return rv;

	/* preserve original error code! */
  err2:
	/* use rcb functions that don't do check the state */
	if (!inwaitq)
		(void) sm_rcb_close_decn(pmss_ctx->pmss_com.rcbcom_rdrcb);
  error:
	/* open rcb for receiving next record */
	if (!inwaitq)
		(void) sm_rcb_open_rcvn(pmss_ctx->pmss_com.rcbcom_rdrcb);

#if 0
  errret:
#endif /* 0 */
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, stat=%d, rt=%x, l=%d, v=%d, ret=%x, rv=%x, inwaitq=%d\n",
pmss_ctx->pmss_id, pmss_ctx->pmss_status, rt, l, v, ret, rv, inwaitq));
	if (rcbe != NULL)
		sm_rcbe_free(rcbe);
	if (inwaitq) {
		if (sm_is_err(rv))	/* shouldn't happen! */
PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, rv=%x, inwaitq=%d\n", rv, inwaitq));
		return PMILT_R_ASYNC;
	}
	return rv;
}

/*
**  SM_SMTPS2PMILT -- PMILT - SMTPS interface
**
**	Parameters:
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
**
**	Called by: sm_pmilt_smtps() for read events
*/

sm_ret_T
sm_smtps2pmilt(sm_evthr_task_P tsk)
{
	int fd, r;
#if PMILT_STORE_FDS
	int i;
#endif /* PMILT_STORE_FDS */
	sm_ret_T ret;
	pmg_ctx_P pmg_ctx;
	pmss_ctx_P pmss_ctx;

	SM_IS_EVTHR_TSK(tsk);
	pmss_ctx = (pmss_ctx_P) tsk->evthr_t_actx;
	pmg_ctx = pmss_ctx->pmss_pmg_ctx;
	fd = tsk->evthr_t_fd;	/* checked in caller */
	ret = sm_rcb_rcv(fd, pmss_ctx->pmss_com.rcbcom_rdrcb, PMSS_RC_MINSZ);

PM_LEV_DPRINTF(6, (PM_DEBFP, "sev=DBG, func=sm_smtps2pmilt, fd=%d, got ret=%x, buf=%d, len=%d\n", fd, ret,
pmss_ctx->pmss_com.rcbcom_rdrcb->sm_rcb_base[0], sm_rcb_getlen(pmss_ctx->pmss_com.rcbcom_rdrcb)));
	if (ret > 0)
		return EVTHR_WAITQ;
	else if (ret == 0) {
		ret = sm_rcb_close_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb);

		/* start appropriate function ... */
		ret = sm_pmss_react(pmss_ctx, tsk);
		if (sm_is_err(ret))
			goto termit;	/* too harsh? */
		else if (PMILT_R_WAITQ == ret)
			return EVTHR_WAITQ;
		else if (PMILT_R_ASYNC == ret)
			return EVTHR_OK;
		else if (EVTHR_DEL == ret)
			goto termit;	/* terminate this SMTPS */
		else
			return ret;
	}
	else if (SM_IO_EOF == ret) {
		ret = sm_rcb_close_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb);
  termit:
		close(fd);

		r = pthread_mutex_lock(&pmg_ctx->pmg_mutex);
		SM_LOCK_OK(r);
		if (r != 0) {
			/* LOG */
			goto error;
		}

		/* Need to abort all open sessions */
		(void) sm_pmss_close(pmss_ctx);

		/* XXX don't crash while holding lock? */
		SM_ASSERT(pmg_ctx->pmg_ssnfd > 0);

		/* free sss ctx */
		pmg_ctx->pmg_ssnfd--;
		pmg_ctx->pmg_sused &= ~(pmss_ctx->pmss_bit);
		if (pmss_ctx->pmss_com.rcbcom_rdrcb != NULL)
			(void) sm_rcb_close_decn(pmss_ctx->pmss_com.rcbcom_rdrcb);

		r = pthread_mutex_unlock(&pmg_ctx->pmg_mutex);
		return EVTHR_DEL;
	}
	else { /* if (ret < 0) */
PM_LEV_DPRINTF(2, (PM_DEBFP, "sev=ERROR, func=sm_smtps2pmilt, ret=%x, errno=%d\n", ret, errno));
	}
PM_LEV_DPRINTF(2, (PM_DEBFP, "sev=ERROR, func=sm_smtps2pmilt, fd=%d\n", fd));

 error:
	return EVTHR_DEL;
}


syntax highlighted by Code2HTML, v. 0.9.1