/*
 * Copyright (c) 2002-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: clt2ar.c,v 1.93 2007/06/03 17:03:04 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/memops.h"
#include "sm/io.h"
#include "sm/net.h"
#include "sm/rcb.h"
#include "sm/mta.h"
#include "sm/rfc2821.h"
#include "smar.h"
#include "reverse.h"
#include "log.h"
#include "sm/qmgrcomm.h"
#include "sm/reccom.h"

/*
**  SMAR_SET_MAX_THRDS -- set maximum number of threads
**
**	Parameters:
**		smar_ctx -- SMAR context
**
**	Returns:
**		usual sm_error code
**
**	input: smar_ctx->smar_max_reqs
**	Side Effects: may change number of threads in evthr and values
**		in smar_ctx
**
**	Last code review:
**	Last code change:
*/

static sm_ret_T
smar_set_max_threads(smar_ctx_P smar_ctx)
{

#if SMAR_SET_MAX_THREADS
	SMAR_LEV_DPRINTF(4, (SMAR_DEBFP,
		"sev=DBG, func=smar_set_max_threads, max_reqs=%u, max_thrds_s=%u, max_thrds_h=%u\n",
		smar_ctx->smar_max_reqs, smar_ctx->smar_max_thrds_s,
		smar_ctx->smar_max_thrds_h));

	if (smar_ctx->smar_max_reqs <= smar_ctx->smar_max_thrds_s)
		return SM_SUCCESS;
	smar_ctx->smar_max_thrds_s = smar_ctx->smar_max_reqs;
	if (smar_ctx->smar_max_thrds_s <= smar_ctx->smar_cnf.smar_cnf_maxthr)
		return SM_SUCCESS;
	if (smar_ctx->smar_max_thrds_s > smar_ctx->smar_max_thrds_h)
	{
		smar_ctx->smar_max_thrds_h = smar_ctx->smar_max_thrds_s * 2;

		/* overflow? */
		if (smar_ctx->smar_max_thrds_s > smar_ctx->smar_max_thrds_h)
			smar_ctx->smar_max_thrds_h = UINT_MAX;
		evthr_set_max_h(smar_ctx->smar_ev_ctx,
			smar_ctx->smar_max_thrds_h);
	}
	evthr_set_max_s(smar_ctx->smar_ev_ctx, smar_ctx->smar_max_thrds_s);
#endif /* SMAR_SET_MAX_THREADS */
	return SM_SUCCESS;
}

/*
**  SMAR_REACT -- Decode data received from client and act accordingly
**
**	Parameters:
**		smar_clt_ctx -- SMAR client context
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
**
**	Last code review:
**	Last code change:
*/

static sm_ret_T
smar_react(smar_clt_ctx_P smar_clt_ctx, sm_evthr_task_P tsk)
{
	uint32_t v, l, rt, tl;
	sm_ret_T ret, res;
	sm_rcb_P rcb;
	sm_rcbe_P rcbe;
	bool inwaitq;
	smar_rcpt_P smar_rcpt;
	smar_rcpts_P smar_rcpts;
	smar_ctx_P smar_ctx;

	SM_IS_SMAR_CLT_CTX(smar_clt_ctx);
	smar_ctx = smar_clt_ctx->smac_ar_ctx;
	SM_IS_SMAR_CTX(smar_ctx);
	ret = res = SM_SUCCESS;
	inwaitq = false;
	smar_rcpt = NULL;
	smar_rcpts = NULL;
	rcbe = NULL;

	/* Decode rcb */
	rcb = smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb;
	ret = sm_rcb_open_dec(rcb);
	if (sm_is_err(ret))
		goto error;

	/* Total length of record */
	ret = sm_rcb_getuint32(rcb, &tl);
	if (sm_is_err(ret) || tl > QM_AR_MAX_REC_LEN || tl > sm_rcb_getlen(rcb)) {
		res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
		goto err2;
	}

	/* protocol header: version */
	ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
	if (sm_is_err(ret) || l != 4 || rt != RT_PROT_VER || v != PROT_VER_RT) {
		res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
		goto err2;
	}

	/* what's the protocol? */
/*
QMGR -> AR:
RT_Q2R_RCPT_ID: rcpt_id
RT_Q2R_RCPT_PA: rcpt_pa
RT_Q2R_FLAGS: flags for resolver

SMTPS -> AR:
RT_S2A_NID	 SMTPS id
RT_S2A_ID	 SMTPS id
RT_S2A_TAID	 transaction id
RT_S2A_RCPT	 rcpt to
RT_S2A_RCPT_IDX	 rcpt idx
RT_S2A_LTYPE	 lookup type
RT_S2A_LFLAGS	 lookup flags
*/

	ret = sm_rcb_get2uint32(rcb, &l, &rt);
	if (sm_is_err(ret)) {
		res = ret;
		goto err2;
	}

	/* SMTPS -> AR; this is slightly ugly, put it into a different fct? */
	if (l == 4 && rt == RT_S2A_NID)
	{
SMAR_LEV_DPRINTF(2, (SMAR_DEBFP, "sev=DBG, func=smar_react, got=RT_S2A_NID\n"));
		ret = sm_rcb_getuint32(rcb, &v);
		if (sm_is_err(ret)) {
			res = ret;
			goto err2;
		}

		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_S2A_MAXTHRDS) {
SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=DBG, func=smar_react, l=%d, rt=%X, expected=RT_S2A_MAXTHRDS\n", l, rt));
			res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
			goto err2;
		}

		/* SM_ASSERT(smar_clt_ctx->smac_idx >= 0); */
		SM_ASSERT(smar_clt_ctx->smac_idx <
			SM_ARRAY_SIZE(smar_ctx->smar_clt_reqs));
		smar_ctx->smar_clt_reqs[smar_clt_ctx->smac_idx] = v;
		smar_ctx->smar_max_reqs += v;
		(void) smar_set_max_threads(smar_ctx);
		/* ignore return value... */

		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_S2A_FLAGS) {
			res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
			goto err2;
		}
		smar_clt_ctx->smac_cl_flags = v;
		if (SM_IS_FLAG(smar_clt_ctx->smac_cl_flags, SMARCL_FL_ACC) &&
		    !SMAR_IS_FLAG(smar_ctx, SMAR_FL_HASACCESS))
		{
			sm_log_write(smar_ctx->smar_lctx,
				AR_LCAT_COMM, AR_LMOD_FROM_QMGR,
				SM_LOG_ERROR, 2,
				"sev=ERROR, func=smar_react, where=startup, status=access map requested but not available");
		}

		/* question: Send a reply?? e.g., "connection accepted"? */
		goto done;
	}
	else if (l == 4 && rt == RT_S2A_ID) {
		smar_addr_P smar_addr;

		/* from SMTPS.... */
		/* do something about it */

		smar_addr = NULL;
		ret = smar_addr_new(smar_clt_ctx, &smar_addr);
		if (sm_is_err(ret))
			goto err_a;

		ret = sm_rcb_getuint32(rcb, &v);
		if (sm_is_err(ret)) {
			res = ret;
			goto err2;
		}
		smar_addr->ara_id = v;

		ret = sm_rcb_get2uint32(rcb, &l, &rt);
		if (sm_is_err(ret) || l != SMTP_STID_SIZE
		    || (rt != RT_S2A_TAID && rt != RT_S2A_SEID))
		{
			res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
			goto err_a;
		}

		ret = sm_rcb_getn(rcb, (uchar *) (smar_addr->ara_taid), l);
		if (sm_is_err(ret))
			goto err_a;

		ret = sm_rcb_get2uint32(rcb, &l, &rt);
		if (sm_is_err(ret)
		    || (rt != RT_S2A_RCPT && rt != RT_S2A_MAIL
		        && rt != RT_S2A_CLT_A && rt != RT_S2A_CERTISS
		        && rt != RT_S2A_EHLO
		       )
		   )
		{
			res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
			goto err_a;	/* check also l? too large? */
		}
		ret = sm_rcb_getnstr(rcb, &smar_addr->ara_pa, l);
SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_react, pa=%S, ret=%r\n", smar_addr->ara_pa, ret));
		if (sm_is_err(ret))
			goto err_a;

		if (RT_S2A_RCPT == rt) {
			ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
			if (sm_is_err(ret) || l != 4 || rt != RT_S2A_RCPT_IDX) {
				res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
				goto err_a;
			}
		}
		else
			v = 0;	/* dummy rcpt index... use a different one? */
		smar_addr->ara_rcpt_idx = (rcpt_idx_T) v;

		/* lookup type and flags are transferred in one entry */
		ret = sm_rcb_get4uint32(rcb, &l, &rt, &v, &tl);
		if (sm_is_err(ret) || l != 8 || rt != RT_S2A_LTYPE) {
			res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
			goto err_a;
		}
		smar_addr->ara_ltype = v;
		smar_addr->ara_lflags = tl;

		/* don't check DNS BLs if none is configured */
		if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_DNSBL) &&
		    !SMAR_IS_FLAG(smar_ctx, SMAR_FL_HASDNSBL))
		{
			SMARA_CLR_LFLAG(smar_addr, SMARA_LFL_DNSBL);
#if 0
			/* currently SMTPS always requests this */
			if (!SMAR_IS_FLAG(smar_ctx, SMAR_FL_DNSBLCOMPL)) {
				sm_log_write(smar_ctx->smar_lctx, AR_LCAT_COMM,
					AR_LMOD_FROM_QMGR, SM_LOG_ERROR, 2,
					"sev=ERROR, func=smar_react, status=dnsbl requested but not available");

				/* NOT locked! */
				SMAR_SET_FLAG(smar_ctx, SMAR_FL_DNSBLCOMPL);
			}
#endif
		}

		/* don't try greylisting if it isn't configured */
		if (SM_IS_FLAG(smar_addr->ara_ltype,  SMARA_LT_GREY) &&
		    !SMAR_IS_FLAG(smar_ctx, SMAR_FL_HASGREY))
		{
			SM_CLR_FLAG(smar_addr->ara_ltype,  SMARA_LT_GREY);
			if (!SMAR_IS_FLAG(smar_ctx, SMAR_FL_GREYCOMPL)) {
				sm_log_write(smar_ctx->smar_lctx, AR_LCAT_COMM,
					AR_LMOD_FROM_QMGR, SM_LOG_ERROR, 2,
					"sev=ERROR, func=smar_react, status=greylisting requested but not available");

				/* NOT locked! */
				SMAR_SET_FLAG(smar_ctx, SMAR_FL_GREYCOMPL);
			}
		}

		/* don't try access map if it isn't configured */
		if (SM_IS_FLAG(smar_addr->ara_ltype, SMAR_LT_ACCESS) &&
		    !SMAR_IS_FLAG(smar_ctx, SMAR_FL_HASACCESS))
		{
			if (!SMAR_IS_FLAG(smar_ctx, SMAR_FL_ACCESSCOMPL)) {
				sm_log_write(smar_ctx->smar_lctx,
					AR_LCAT_COMM, AR_LMOD_FROM_QMGR,
					SM_LOG_ERROR, 2,
					"sev=ERROR, func=smar_react, status=access map requested but not available");

				/* NOT locked! */
				SMAR_SET_FLAG(smar_ctx, SMAR_FL_ACCESSCOMPL);
			}
		}

		while (!SM_RCB_ISEOB(rcb)) {
			ret = sm_rcb_get2uint32(rcb, &l, &rt);
			if (sm_is_err(ret)) {
				res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
				goto err_a;
			}
			switch (rt)
			{
			  case RT_S2A_MAIL: /* for protected rcpt */
			  case RT_S2A_CERTSUB:
				ret = sm_rcb_getnstr(rcb, &smar_addr->ara_pa2, l);
				if (sm_is_err(ret))
					goto err_a;
				break;

			  case RT_S2A_IPV4: /* protected rcpt, greylisting */
				if (l != 4) {
					res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
					goto err_a;
				}
				ret = sm_rcb_getuint32(rcb, &v);
				if (sm_is_err(ret))
					goto err_a;
				smar_addr->ara_ipv4 = v;
				SMARA_SET_FLAG(smar_addr, SMARA_FL_RCVDIPV4);
				break;

			  case RT_S2A_TIME: /* greylisting */
				if (8 == l) {
					uint64_t ul;

					ret = sm_rcb_getuint64(rcb, &ul);
					if (sm_is_err(ret))
						goto err_a;
					smar_addr->ara_conn_time = ul;
				}
				else if (4 == l) {
					ret = sm_rcb_getuint32(rcb, &v);
					if (sm_is_err(ret))
						goto err_a;
					smar_addr->ara_conn_time = v;
				}
				else {
					res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
					goto err_a;
				}
				break;

			  default:
				res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
				goto err_a;
			}
		}

		ret = sm_rcbcom_prerep(&smar_clt_ctx->smac_com_ctx, tsk, &rcbe);
		if (sm_is_err(ret))
			goto err_a;
		inwaitq = true;

		smar_addr->ara_rcbe = rcbe;

		/* call the correct function based on type! */
		if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_LOCAL))
			ret = smar_addr_check(smar_ctx, smar_addr);
		else
			ret = smar_access_check(smar_ctx, smar_addr);

		if (sm_is_err(ret))
			goto error;	/* need more cleanup here? */

		return SMAR_R_ASYNC;

  err_a:
		smar_addr_free(smar_addr);
		goto error;
	}
	else if (l == SMTP_STID_SIZE && rt == RT_C2A_SEID) {
		/* RT_C2A_* is currently only for t-smar-rvrs-0! */
		smar_rvrs_P smar_rvrs;

		/* from SMTPS.... */
		/* do something about it */

		smar_rvrs = NULL;
		ret = smar_rvrs_new(smar_ctx, smar_clt_ctx, &smar_rvrs);
		if (sm_is_err(ret))
			goto err_c;

		ret = sm_rcb_getn(rcb, (uchar *) (smar_rvrs->arv_seid), l);
		if (sm_is_err(ret))
			goto err_c;

		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_C2A_IPV4) {
			res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
			goto err_c;	/* check also l? too large? */
		}
		smar_rvrs->arv_ipv4 = v;

		/* lookup type and flags are transferred in one entry */
		ret = sm_rcb_get4uint32(rcb, &l, &rt, &v, &tl);
		if (sm_is_err(ret) || l != 8 || rt != RT_C2A_LTYPE) {
			res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
			goto err_c;
		}
		smar_rvrs->arv_ltype = v;
		smar_rvrs->arv_lflags = tl;
		smar_rvrs->arv_cbf = smar_rvrs_re;

		ret = sm_rcbcom_prerep(&smar_clt_ctx->smac_com_ctx, tsk, &rcbe);
		if (sm_is_err(ret))
			goto err_c;
		inwaitq = true;

		smar_rvrs->arv_rcbe = rcbe;

		ret = smar_rvrs_rslv(smar_ctx, smar_rvrs, 0u);

		if (sm_is_err(ret))
			goto err_c;	/* need more cleanup here? */

		return SMAR_R_ASYNC;

  err_c:
		smar_rvrs_free(smar_rvrs);
		goto error;
	}

	if (l != SMTP_RCPTID_SIZE || rt != RT_Q2R_RCPT_ID)
		goto err2;
	ret = smar_rcpts_new(smar_ctx, smar_clt_ctx, &smar_rcpts);
	if (sm_is_err(ret))
		goto err2;
	ret = smar_rcpt_new(&smar_rcpt);
	if (sm_is_err(ret))
		goto err2;
	smar_rcpts->arrs_rcpt = smar_rcpt;
	SMARR_SET_FLAG(smar_rcpt, SMARR_FL_ORCPT);
	ret = sm_rcb_getn(rcb, (uchar *) (smar_rcpt->arr_id), l);
	if (sm_is_err(ret))
		goto err2;
	RCPT_ID_COPY(smar_rcpts->arrs_rcpt_id, smar_rcpt->arr_id);

	ret = sm_rcb_get2uint32(rcb, &l, &rt);
	if (sm_is_err(ret) || rt != RT_Q2R_RCPT_PA) { /* check l? too large? */
		res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
		goto err2;
	}

	ret = sm_rcb_getnstr(rcb, &smar_rcpt->arr_pa, l);
SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_react, pa=%S, ret=%r\n", smar_rcpt->arr_pa, ret));
	if (sm_is_err(ret))
		goto err2;

	ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
	if (sm_is_err(ret) || l != 4 || rt != RT_Q2R_FLAGS) {
SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=ERROR, func=smar_react, rt=%#x, v=%d, l=%d, ret=%r, res=%r\n", rt, v, l, ret, res));
		res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
		goto err2;
	}
	smar_rcpt->arr_rqflags = v;
	smar_rcpt->arr_cnf_lfl = SMMAP_LFL_ALL|SMMAP_LFL_NOAT; /* default */
	if (!SM_RCB_ISEOB(rcb) &&
		sm_is_success(sm_rcb_peek2uint32(rcb, &l, &rt)) &&
		4 == l && RT_R2Q_RCPT_CNF_LKP_FL == rt)
	{
		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_R2Q_RCPT_CNF_LKP_FL) {
SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=ERROR, func=smar_react, rt=%#x, v=%d, l=%d, ret=rx, res=rx\n", rt, v, l, ret, res));
			res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
			goto err2;
		}
		smar_rcpt->arr_cnf_lfl = v;
	}

	if (!SM_RCB_ISEOB(rcb)) {
		ret = sm_rcb_get3uint32(rcb, &l, &rt, &v);
		if (sm_is_err(ret) || l != 4 || rt != RT_Q2R_TIMEOUT) {
SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=ERROR, func=smar_react, rt=%#x, v=%d, l=%d, ret=rx, res=rx\n", rt, v, l, ret, res));
			res = sm_error_perm(SM_EM_AR_Q2AR, EINVAL);
			goto err2;
		}
		smar_rcpt->arr_timeout = v;
	}

	ret = sm_rcbcom_prerep(&smar_clt_ctx->smac_com_ctx, tsk, &rcbe);
	if (sm_is_err(ret)) {
SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=ERROR, func=smar_react, sm_rcbcom_prerep=%r\n", ret));
		goto err0;
	}
	inwaitq = true;
	res = SMAR_R_ASYNC;

	/* XXX Check proper cleanup of data: who free()s what? */
	SMARR_SET_FLAG(smar_rcpt, SMARR_FL_A4MT);
	smar_rcpts->arrs_rcbe = rcbe;
	ret = smar_rcpt_rslv(smar_ctx, smar_rcpts);
	smar_rcpt = NULL;	/* smar_rcpt_rslv() took over responsibility */
	smar_rcpts = NULL;	/* smar_rcpt_rslv() took over responsibility */
	if (sm_is_err(ret)) {
		/* in which case is this a fatal error? none? */
SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=ERROR, func=smar_react, smar_rcpt_rslv=%r\n", ret));

		/* need more cleanup here? */
		goto errret;
	}
	return SMAR_R_ASYNC;

  done:
	ret = sm_rcb_close_dec(smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb);
	(void) sm_rcb_open_rcv(smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb);
	if (ret == SM_SUCCESS && inwaitq)
		return SMAR_R_ASYNC;
	return ret;

	/* preserve original error code! */
  err2:
	if (!inwaitq) {
		/* use rcb functions that don't check the state */
		(void) sm_rcb_close_decn(smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb);
	}
  error:
	if (!inwaitq) {
		/* open rcb for receiving next record */
		(void) sm_rcb_open_rcvn(smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb);
	}
  err0:
	(void) smar_rcpt_free(smar_rcpt, NULL);
	(void) smar_rcpts_free(smar_rcpts);

  errret:
	sm_log_write(smar_ctx->smar_lctx, AR_LCAT_COMM,
		AR_LMOD_FROM_QMGR, SM_LOG_ERROR, 0,
		"sev=ERROR, func=smar_react, status=%#x, rt=%#x, v=%d, l=%d, ret=%m, res=%m",
		smar_ctx->smar_status, rt, v, l, ret, res);
	return res;
}

/*
**  SMAR_CLT_CLOSE -- close connection to client
**
**	Parameters:
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
smar_clt_close(sm_evthr_task_P tsk)
{
	smar_clt_ctx_P smar_clt_ctx;
	smar_ctx_P smar_ctx;

	SM_IS_EVTHR_TSK(tsk);
	smar_clt_ctx = (smar_clt_ctx_P) tsk->evthr_t_actx;
	SM_IS_SMAR_CLT_CTX(smar_clt_ctx);
	smar_ctx = smar_clt_ctx->smac_ar_ctx;
	SM_IS_SMAR_CTX(smar_ctx);
	close(tsk->evthr_t_fd);

	/* See comment in qm_fr_ss()!! */
	tsk->evthr_t_fd = INVALID_FD;

	smar_ctx->smar_clt_used &= ~(smar_clt_ctx->smac_bit);
	smar_clt_ctx->smac_bit = 0;
	smar_clt_ctx->smac_status = SMAC_ST_NONE;

	/* "close" client context... put this into a function? */
	/* SM_ASSERT(smar_clt_ctx->smac_idx >= 0); */
	SM_ASSERT(smar_clt_ctx->smac_idx < SM_ARRAY_SIZE(smar_ctx->smar_clt_reqs));
	smar_ctx->smar_max_reqs -= smar_ctx->smar_clt_reqs[smar_clt_ctx->smac_idx];
	smar_ctx->smar_clt_reqs[smar_clt_ctx->smac_idx] = 0;
	smar_clt_ctx->smac_idx = UINT32_MAX;
	/* more cleanup? */

	sm_rcb_close_decn(smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb);

	smar_clt_ctx->smac_com_ctx.rcbcom_tsk = NULL;
	return SM_SUCCESS;
}

/*
**  SMAR_CLT2AR -- client - AR interface
**	invoked by smar_clt() to deal with RCB receiption
**
**	Parameters:
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
smar_clt2ar(sm_evthr_task_P tsk)
{
	int fd;
	sm_ret_T ret;
	smar_clt_ctx_P smar_clt_ctx;
	smar_ctx_P smar_ctx;

	SM_IS_EVTHR_TSK(tsk);
	smar_clt_ctx = (smar_clt_ctx_P) tsk->evthr_t_actx;
	SM_IS_SMAR_CLT_CTX(smar_clt_ctx);
	smar_ctx = smar_clt_ctx->smac_ar_ctx;
	SM_IS_SMAR_CTX(smar_ctx);

	fd = tsk->evthr_t_fd;	/* checked in caller */
	ret = sm_rcb_rcv(fd, smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb,
			QSS_RC_MINSZ);

	SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "sev=DBG, func=smar_clt2ar, fd=%d, ret=%r, buf=%d, len=%d, smar_status=%#x\n",
		fd, ret, smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb->sm_rcb_base[0], sm_rcb_getlen(smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb), smar_ctx->smar_status));
	if (ret > 0) {
#if 0
		if (buf[0] == 'Q')
			return EVTHR_TERM|EVTHR_DEL;
#endif /* 0 */
		return EVTHR_WAITQ;
	}
	else if (0 == ret) {
		ret = sm_rcb_close_rcv(smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb);

		/* start appropriate function ... */
		ret = smar_react(smar_clt_ctx, tsk);
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "sev=DBG, func=smar_clt2ar, sm_smar_react=%r\n", ret));
		if (sm_is_err(ret))
			goto termit;	/* too harsh? */
		else if (SMAR_R_WAITQ == ret)
			return EVTHR_WAITQ;
		else if (SMAR_R_ASYNC == ret)
			return EVTHR_OK;
		else if (EVTHR_DEL == ret)
			goto termit;	/* terminate this client */
		else
			return ret;
	}
	else if (SM_IO_EOF == ret) {
		ret = sm_rcb_close_rcv(smar_clt_ctx->smac_com_ctx.rcbcom_rdrcb);
  termit:
SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_clt2ar, task=%p, status=terminate, ret=%r\n", smar_clt_ctx->smac_com_ctx.rcbcom_tsk, ret));
		(void) smar_clt_close(tsk);
		return EVTHR_DEL;
	}
	else /* if (ret < 0) */
	{
		sm_log_write(smar_ctx->smar_lctx,
			AR_LCAT_COMM, AR_LMOD_FROM_QMGR,
			SM_LOG_ERROR, 0,
			"sev=ERROR, func=smar_clt2ar, ret=%m, errno=%d"
			, ret, errno);
		goto termit;
	}
	sm_log_write(smar_ctx->smar_lctx,
		AR_LCAT_COMM, AR_LMOD_FROM_QMGR,
		SM_LOG_ERROR, 0,
		"sev=ERROR, func=smar_clt2ar, fd=%d", fd);

#if 0
 error:
#endif
	return EVTHR_DEL;
}

/*
**  SMAR_AR2CLT -- SMAR 2 client (QMGR/SMTPS) interface
**
**	Parameters:
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
*/

static sm_ret_T
smar_ar2clt(sm_evthr_task_P tsk)
{
	smar_clt_ctx_P smar_clt_ctx;

	SM_IS_EVTHR_TSK(tsk);
	smar_clt_ctx = (smar_clt_ctx_P) tsk->evthr_t_actx;
	SM_IS_SMAR_CLT_CTX(smar_clt_ctx);
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "smar_ar2clt: tsk=%p, fd=%d\n", tsk, tsk->evthr_t_fd));
	return sm_rcbcom2mod(tsk, &smar_clt_ctx->smac_com_ctx);
}

/*
**  SMAR_CLT -- SMAR - client interface
**	This runs as a task.
**
**	Parameters:
**		tsk -- evthr task
**
**	Returns:
**		usual sm_error code
*/

sm_ret_T
smar_clt(sm_evthr_task_P tsk)
{
	sm_ret_T ret;

	SM_IS_EVTHR_TSK(tsk);
	if (is_valid_fd(tsk->evthr_t_fd)) {
		/*
		**  Note: The first call is not allowed to put the
		**  task back into the wait queue since then the second
		**  function acts on a "non-locked" tsk.
		*/

		ret = EVTHR_WAITQ;
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "sev=DBG, func=smar_clt, tsk=%p, flags=%#x, fd=%d\n", tsk, tsk->evthr_t_evocc, tsk->evthr_t_fd));
		if (evthr_got_wr(tsk)) {
			ret = smar_ar2clt(tsk);
			if (sm_is_err(ret))
				goto error;
		}
		if (evthr_got_rd(tsk)) {
			/*
			**  This routine could set EVTHR_EV_WR
			**  to cause data to be written back to the SMTPS.
			**  However, most of the routines may work
			**  asynchronously and hence they should return the
			**  correct value themselves.
			*/

			ret = smar_clt2ar(tsk);
			if (sm_is_err(ret))
				goto error;
		}
		return ret;
	}
	return EVTHR_DEL;

  error:
	(void) smar_clt_close(tsk);
	return EVTHR_DEL;
}


syntax highlighted by Code2HTML, v. 0.9.1